prick 0.2.0 → 0.7.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -5
  3. data/Gemfile +4 -1
  4. data/TODO +10 -0
  5. data/doc/prick.txt +114 -0
  6. data/exe/prick +328 -402
  7. data/lib/ext/fileutils.rb +18 -0
  8. data/lib/ext/forward_method.rb +18 -0
  9. data/lib/ext/shortest_path.rb +44 -0
  10. data/lib/prick.rb +20 -10
  11. data/lib/prick/branch.rb +254 -0
  12. data/lib/prick/builder.rb +164 -0
  13. data/lib/prick/cache.rb +34 -0
  14. data/lib/prick/command.rb +19 -11
  15. data/lib/prick/constants.rb +122 -48
  16. data/lib/prick/database.rb +28 -20
  17. data/lib/prick/diff.rb +125 -0
  18. data/lib/prick/exceptions.rb +15 -3
  19. data/lib/prick/git.rb +77 -30
  20. data/lib/prick/head.rb +183 -0
  21. data/lib/prick/migration.rb +40 -200
  22. data/lib/prick/program.rb +493 -0
  23. data/lib/prick/project.rb +523 -351
  24. data/lib/prick/rdbms.rb +4 -13
  25. data/lib/prick/schema.rb +16 -90
  26. data/lib/prick/share.rb +64 -0
  27. data/lib/prick/state.rb +192 -0
  28. data/lib/prick/version.rb +62 -29
  29. data/libexec/strip-comments +33 -0
  30. data/make_releases +48 -345
  31. data/make_schema +10 -0
  32. data/prick.gemspec +14 -23
  33. data/share/diff/diff.after-tables.sql +4 -0
  34. data/share/diff/diff.before-tables.sql +4 -0
  35. data/share/diff/diff.tables.sql +8 -0
  36. data/share/migration/diff.tables.sql +8 -0
  37. data/share/migration/features.yml +6 -0
  38. data/share/migration/migrate.sql +3 -0
  39. data/share/migration/migrate.yml +8 -0
  40. data/share/migration/tables.sql +3 -0
  41. data/share/schema/build.yml +14 -0
  42. data/share/schema/schema.sql +5 -0
  43. data/share/schema/schema/build.yml +3 -0
  44. data/share/schema/schema/prick/build.yml +14 -0
  45. data/share/schema/schema/prick/data.sql +7 -0
  46. data/share/schema/schema/prick/schema.sql +5 -0
  47. data/share/{schemas/prick/schema.sql → schema/schema/prick/tables.sql} +2 -5
  48. data/{file → share/schema/schema/public/.keep} +0 -0
  49. data/share/schema/schema/public/build.yml +14 -0
  50. data/share/schema/schema/public/schema.sql +3 -0
  51. data/test_assorted +192 -0
  52. data/test_feature +112 -0
  53. data/test_refactor +34 -0
  54. data/test_single_dev +83 -0
  55. metadata +43 -68
  56. data/lib/prick/build.rb +0 -376
  57. data/lib/prick/migra.rb +0 -22
  58. data/share/schemas/prick/data.sql +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a41038c7beeb2bb1503ccb199cc45622d6fbe28e113003b98e8e78e10658a37
4
- data.tar.gz: 59cb5055863ce2cd63ad9db5a2adf9a0de33d4386bca210dd8e9f25f602bb5e1
3
+ metadata.gz: dd62ad39d6f79354d9eb4e68e102cbe2fa45c3c83f3bb670bb67cc91faab2b6c
4
+ data.tar.gz: 1802956bd55eafd32b16ff2d473e7b8c4f22ffaa2526ab83afb1c8d271b3fe37
5
5
  SHA512:
6
- metadata.gz: 0b19d6f150e840101f60aa9337583257a0f72e36e2eb7a84b1403063b274099e7a27716aecd2ccaaaf09484a4f5d4b1f9da6eebde340fe9b69da8bab8342528e
7
- data.tar.gz: 7b71790136d8de6e2f3c77a58fb5d1e432bbbe6f0c910ada86608e05f759c8ea68d255f1cc038ce2b5efebfb7e379f0bb2f607a94cfd99b5f26cf3665f135130
6
+ metadata.gz: c8b1553cba9e5afe792b1f3119ffb9b85e23626d830b752dc98ee5bd28e4c7eeaf8e7b50acb52cfe9e32dcf935d8dcd99694bf2d1d29ebd830c1a9abcb852f38
7
+ data.tar.gz: 3a189a2dff6750afc58f01189dc86fd671b605f5281e17d725607483e0d2f24603f54cc9071145dcfa681109d971da8736a5e81be454b3b1d3d3ee055060f92f
data/.gitignore CHANGED
@@ -19,10 +19,11 @@
19
19
 
20
20
  # Put your personal ignore files in /home/clr/.config/git/ignore
21
21
 
22
- # Temporary ignores
23
- /releases/
24
- /migrations/
25
- /prick.sh
26
-
27
22
  # Ignore bundle binstubs
28
23
  /bin/prick
24
+
25
+ # Ignore development testing dir
26
+ /dir
27
+
28
+ # Ignore stove-away directory
29
+ /hold
data/Gemfile CHANGED
@@ -1,6 +1,9 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) {|repo_name| "https://github.com/clrgit/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in prick.gemspec
6
6
  gemspec
7
+
8
+ gem "rake", "~> 12.0"
9
+ gem "rspec", "~> 3.0"
data/TODO ADDED
@@ -0,0 +1,10 @@
1
+
2
+ o Check for commits to tags
3
+ o Accumulate version history in prick.versions instead of just having the newest
4
+ o Using rc's in migration syntax: fork-version-fork-version.rc1 ?
5
+ o Make it possible to execute prick in any subdirecty instead of only in the root
6
+ o Hack migra to create separate file(s) for table changes
7
+ o Also execute subject/ directory if only a subject.sql is found. subject.yml
8
+ should still override everything
9
+ o Track included files - this will make dependency checks much easier
10
+
data/doc/prick.txt ADDED
@@ -0,0 +1,114 @@
1
+
2
+
3
+ MIGRATION STATE FILES
4
+ .prick-migration (State file)
5
+ version: Version
6
+ The target version. The result of running the migration on the base
7
+ release
8
+ base_version: Version
9
+ Base version. Running the migration on a base release yields a release
10
+ of with the same version as the `version` property
11
+
12
+ The 0.0.0 version has base_version equal to nil. A Feature has version
13
+ equal to its base version.
14
+
15
+ Note that while a release's .prick-migration file lives in the release
16
+ directory, it's .prick-features file and the migrations lives in the
17
+ directory of the base release. This separation supports the
18
+ single-developer workflow where the next release is not known beforehand
19
+
20
+ .prick-features (State file)
21
+ features: Array
22
+ A list of features versions
23
+
24
+ MIGRATION FILES
25
+ migrate.sql
26
+ features.sql
27
+ diff.sql
28
+
29
+
30
+ MIGRATION FILE STRUCTURE
31
+ releases/
32
+ 0.0.0/
33
+ .prick-migration -> version: 0.0.0, base_version: nil
34
+ .prick-features -> [feature_a]
35
+ feature_a/
36
+ .prick-migration -> version: nil, base_version: 0.0.0
37
+ .prick-features -> []
38
+ feature_b/ (rebased)
39
+ .prick-migration -> version: 0.2.0, base_version: 0.2.0-pre.1
40
+ .prick-features -> []
41
+
42
+ 0.1.0/
43
+ .prick-migration -> version: 0.1.0, base_version: 0.0.0
44
+ feature_b/ symlinks to ../0.0.0/feature_b/
45
+
46
+ 0.1.0/ (wip - single developer work flow)
47
+ .prick-migration -> version: 0.1.0, base_version: 0.0.0
48
+ .prick-features -> [feature_c]
49
+ feature_c/
50
+ ...
51
+
52
+ 0.2.0/ (doing a pre-release)
53
+ .prick-migration -> version: 0.2.0-pre.1, base_version: 0.1.0
54
+
55
+ migrations/
56
+ 0.0.0_0.1.0/
57
+ .prick-migration -> version: 0.1.0, base_version: 0.0.0
58
+ .prick-features -> []
59
+
60
+
61
+ NOTES
62
+ #
63
+ # Distinguish between running a migration 0.1.0 -> 0.2.0 and running the
64
+ # migration in the 0.2.0 directory in the single-developer workflow
65
+ #
66
+ # prick migrate
67
+ # On a tag: Migrate to that tag
68
+ # On a branch: Run migrations in the release directory
69
+ #
70
+ # prick migrate 0.2.0 <- the version number reflects where to find the migration
71
+ # Runs the migration files in the release directory
72
+ #
73
+ # prick migrate 0.1.0
74
+ # Runs the migration files in the base release directory
75
+ #
76
+ #
77
+ # prick build
78
+ # Builds the current schema into the project database
79
+ #
80
+ # prick build 0.1.0
81
+ # Builds the given schema into a versioned database
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+ ######################
96
+
97
+ PRICK FILES
98
+ ./prick-version
99
+ Contains just one line with the version of the prick command that should be
100
+ used with this project (eg. 'prick-0.1.0'). Prick checks this file at
101
+ startup and fails if the required version doesn't match the current command
102
+
103
+ ./prick
104
+ project_name
105
+ Name of the project
106
+
107
+ ./releases/*/.prick
108
+ base_version
109
+ The base version of this release. nil for release 0.0.0
110
+ features
111
+ List of features in a release based on this release. Initially empty
112
+ but filled in when the directory is prepared for a subsequent release.
113
+ To find the set of features in the current release, inspect the prick
114
+ file of the base_version in this branch
data/exe/prick CHANGED
@@ -1,461 +1,335 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'prick.rb'
4
+ require 'prick/program.rb'
4
5
 
5
6
  require 'shellopts'
6
- require 'indented_io'
7
+
7
8
  require 'tempfile'
8
- require 'ext/algorithm.rb'
9
9
 
10
10
  include ShellOpts
11
+ include Prick
11
12
 
12
- # TODO: Backup & restore
13
13
  SPEC = %(
14
- n,name=NAME
15
- C,directory=DIR
16
- h,help
17
- v,verbose
18
- q,quiet
19
- version
20
-
21
- init!
22
- info!
23
- status!
24
- history!
25
-
26
- checkout!
27
-
28
- feature!
29
- rebase!
30
-
31
- create! create.release! create.prerelease! create.feature! create.migration!
32
- increment! increment.prerelease!
33
-
34
- prepare! prepare.release! prepare.prerelease! prepare.feature! prepare.migration!
35
- include!
36
- commit!
37
- prerelease!
38
- release!
39
-
40
- list! list.databases! list.releases! list.prereleases!
41
- build!
42
- use! f,file=FILE
43
- load! f,file=FILE
44
- reload! f,file=FILE
45
- unload!
46
- clean!
47
-
48
- migrate!
49
- )
50
-
51
- USAGE = "-h -v -n NAME -C DIR COMMAND"
52
-
53
- HELP = %(
54
- NAME
55
- prick - Database version management
56
-
57
- USAGE
58
- prick -v -n NAME -C DIR -h <command>
59
-
60
- OPTIONS
61
- -n, --name=NAME
14
+ -n,name=NAME
62
15
  Name of project. Defauls to the environment variable `PRICK_PROJECT` if
63
16
  set and else the name of the current directory
64
17
 
65
- -C, --directory=DIR
66
- Change to directory DIR before anything else
18
+ -C,directory=DIR
19
+ Change to directory DIR before doing anything else
67
20
 
68
- -h, --help
21
+ -h,help COMMAND...
69
22
  Print this page
70
23
 
71
- -v, --verbose
72
- Be verbose
24
+ +v,verbose
25
+ Be verbose. Repeated --verbose options increase the verbosity level
73
26
 
74
- --version
75
- Print prick version
76
-
77
- COMMANDS
78
- INFO COMMANDS
79
- init [DIR]
80
- Initialize a project directory
27
+ -q,quiet
28
+ Be quiet
81
29
 
82
- info
83
- Print project information
84
-
85
- status
86
- Short status
87
-
88
- GENERAL COMMANDS
89
- checkout
90
- As git checkout but does some extra work behind the scenes
91
-
92
- migrate
93
- Migrate from the current branch in the database to the current branch
94
-
95
-
96
- DEVELOPER COMMANDS
97
- create feature FEATURE
98
- Create a new feature branch and a feature directory under
99
- features/<base-version>. Note that the base version is the base version
100
- of the pre-release
101
-
102
- Features are named <feature>-<base-version>
103
-
104
- prepare migration
105
- Create migration files from the base release to the current schema. It
106
- does not overwrite existing migration files
107
-
108
- rebase VERSION
109
- Rebase the feature to HEAD of the release. The rebased feature lives on
110
- a branch of its own and have a symlink from the new base release to the
111
- feature implementation in the original release. Rebased features are
112
- named <feature>-<rebase-version>
113
-
114
-
115
- RELEASE MANAGER COMMANDS
116
- migrate VERSION|patch|minor|major
117
- Prepare migration from current release to the given version. The branch
118
- will be named <old-version>_<new-version>. Note that the generated
119
- files don't include data migrations so it may be easier to first create a
120
- prerelease and include missing features and then use that release as base
121
- for an empty migration release that just changes the version number
30
+ --version
31
+ Print prick version
122
32
 
123
- (need some thought)
33
+ init! -u,user=USER [DIR]
34
+ Initialize a project in the given directory. DIR defaults to the current
35
+ directory. The USER is the postgres user, it defaults to the project name
124
36
 
125
- prepare VERSION|patch|minor|major
126
- Prepare a new release by branching to <version>.pre.0 and creating a
127
- features directory for the _base_ version. If version is one of the
128
- names patch, minor, or major then the version is constructed by
129
- incrementing the given part of the base release's version
37
+ info!
38
+ Print project information
130
39
 
131
- include FEATURE
132
- Merge a feature branch into the prepared release
40
+ list.releases! -m,migrations -c,cancelled
41
+ List releases. Include migration releases if the --migration option is
42
+ present and also include cancelled releases if the --cancelled option is
43
+ present
133
44
 
134
- prerelease
135
- Bumps the pre-release serial and create a new branch
45
+ list.migrations!
46
+ List migrations
136
47
 
137
- release [VERSION]
138
- Create a new release. The version is not used if the release was prepared
48
+ list.upgrades! [FROM [TO]]
49
+ List available upgrades
139
50
 
51
+ list.cache!
52
+ List cache files
140
53
 
141
- MANIPULATING DATABASES AND RELEASES
142
- list [databases|releases|prereleases]
143
- Output a list of databases, releases, or prereleases. List databases by
144
- default
54
+ build! -d,database=DATABASE -C,no-cache [TAG]
55
+ Build the current database from the content in the schemas/ directory.
56
+ With a tag the version is built into the associated versioned database
57
+ and the result is saved to cache unless the -C option is given. The -d
58
+ option overrides the default database
145
59
 
146
- build [VERSION]
147
- Build the schema in the project database. If a version is given, the
148
- version is built into the versioned database and the release cached
149
- on disk
60
+ make! -d,database=DATABASE -C,no-cache [TAG]
61
+ Build the current database from the content in the schemas/ directory.
62
+ With a tag the associated versioned database is loaded from cache if
63
+ present. The -C option ignores the cache and the -d option overrides
64
+ the default database
150
65
 
151
- load
152
- Load the current cache into the project database
66
+ make.clean! -a,all
67
+ Drop versioned databases and remove cached and other temporary files.
68
+ Also drop the project database if the -a option is given
153
69
 
154
- load -f FILE [VERSION]
155
- Load the cached release into its versioned database. It is an error if
156
- the release isn't cached - use build to create it
157
-
158
- reload [VERSION]
159
- Clear the versioned database and reload the release from the cache
70
+ load! -d,database=DATABASE VERSION|FILE
71
+ Load the cached version or the file into the associated versioned
72
+ database. It is an error if the version hasn't been cached. The --database
73
+ argument overrides the database
160
74
 
161
- unload [VERSION]
162
- Delete the versioned database. Default is to delete all project related
163
- databases
75
+ save! VERSION [FILE]
76
+ Save the versioned database associated with version to the cache or the
77
+ given file
164
78
 
165
- clean [VERSION]
166
- Delete the cached release. Default is to delete all cached releases
79
+ drop! -a,all [DATABASE]
80
+ Drop the given database or all versioned databases. The --all option also
81
+ drops the project database
167
82
 
83
+ diff! -m,mark -t,tables -T,notables
84
+ diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]]
85
+ Create a schema diff between the given databases or versions. Default
86
+ to-version is the current schema and default from-version is the base
87
+ version of this branch/tag
168
88
 
89
+ migrate!
90
+ Not yet implemented
91
+
92
+ prepare!
93
+ Prepare a release. Just a shorthand for 'prick prepare release'
94
+
95
+ prepare.release! [FORK]
96
+ Populate the current migration directory with migration files
97
+
98
+ prepare.feature! NAME
99
+ Create and populate a feature as a subdirectory of the current directory.
100
+ Also prepares the current release directory
101
+
102
+ prepare.migration! [FROM]
103
+ Create and populate a migration directory
104
+
105
+ prepare.schema! NAME
106
+ Create and populate a new schema directory. Existing files and
107
+ directories are kept
108
+
109
+ prepare.diff! [VERSION]
110
+ Not yet implemented
111
+
112
+ include.feature! FEATURE
113
+ Include the given feature in the current pre-release
114
+
115
+ check!
116
+ Check that the current migration applied to the base version yields the
117
+ same result as loading the current schema
118
+
119
+ create.release! [RELEASE]
120
+ Prepare a release and create release directory and migration file before
121
+ tagging and branching to a release branch. The RELEASE argument can be
122
+ left out if the current branch is a prerelease branch
123
+
124
+ create.prerelease! RELEASE
125
+ Prepare a release and create release directory and migration file before
126
+ branching to a prerelease branch
127
+
128
+ create.feature! NAME
129
+ Prepare a feature before branching to a feature branch
130
+
131
+ cancel!
132
+ Cancel a release. Just a shorthand for 'prick cancel release'
133
+
134
+ cancel.release!
135
+ Cancel a release. Since tags are immutable, the release is cancelled by
136
+ added a special cancel-tag to the release that makes prick ignore it
137
+
138
+ generate.migration!
139
+ Create a script to migrate the database
140
+
141
+ generate.schema!
142
+ Create a script to create the database
143
+
144
+ upgrade!
145
+ Migrate the database to match the current schema
146
+
147
+ backup! [FILE]
148
+ Saves a backup of the database to the given file or to the var/spool
149
+ directory
150
+
151
+ restore! [FILE]
152
+ Restore the database from the given backup file or from the latest backup
153
+ in the var/spool directory
169
154
  )
170
155
 
171
- INFO_PARAMS = {
172
- "Releases": :releases,
173
- "Pre-releases": :prereleases,
174
- "Features": :features,
175
- "Ignored releases": :ignored_release_nodes,
176
- "Ignored features": :ignored_feature_nodes,
177
- "Orphan disk features": :orphan_feature_nodes,
178
- "Orphan disk releases": :orphan_release_nodes,
179
- "Orphan git branches": :orphan_git_branches
180
- }
181
-
182
- def info(project)
183
- for header, member in INFO_PARAMS
184
- elements = project.send(member)
185
- puts header
186
- if elements.empty?
187
- puts " [none]"
188
- else
189
- if member == :features
190
- project.features.each { |release, features|
191
- puts " #{release}"
192
- features.each { |feature| puts " #{feature.feature}" }
193
- }
194
- else
195
- elements.sort.each { |element| puts " #{element}" }
196
- end
197
- end
198
- end
199
- end
200
156
 
201
- def compute_version(project, arg)
202
- if arg.nil?
203
- nil
204
- elsif %w(major minor patch).include?(arg)
205
- project.release.version.increment(arg.to_sym)
206
- else
207
- Prick::Version.new(arg)
208
- end
209
- end
210
157
 
211
- opts, args = ShellOpts.as_struct(SPEC, ARGV)
158
+ opts, args = ShellOpts.process(SPEC, ARGV)
212
159
 
213
160
  # Handle --help
214
161
  if opts.help?
215
- begin
216
- file = Tempfile.new("prick")
217
- file.puts HELP.split("\n").map { |l| l.sub(/^ /, "") }
218
- file.flush
219
- system "less #{file.path}"
220
- ensure
221
- file.close
222
- end
162
+ ShellOpts.help
223
163
  exit
224
- end
164
+ end
225
165
 
226
166
  # Handle --version
227
- if opts.version?
228
- puts "prick-#{Prick::VERSION}"
229
- exit
167
+ if opts.version?
168
+ puts "prick-#{VERSION}"
169
+ exit
230
170
  end
231
171
 
232
172
  begin
233
173
  # Honor -C option
234
- if opts.directory
235
- begin
236
- Dir.chdir(opts.directory)
237
- rescue Errno::ENOENT
238
- raise Prick::Error, "Can't cd to '#{directory}'"
174
+ if opts.directory?
175
+ if File.exist?(opts.directory)
176
+ begin
177
+ Dir.chdir(opts.directory)
178
+ rescue Errno::ENOENT
179
+ raise Prick::Error, "Can't cd to '#{opts.directory}'"
180
+ end
181
+ else
182
+ raise Prick::Error, "Can't find directory: #{opts.directory}"
239
183
  end
240
184
  end
241
185
 
242
- name = opts.name || ENV["PRICK_PROJECT"] || File.basename(Dir.getwd)
243
- verbose = opts.verbose?
186
+ # Create program object
187
+ program = Program.new(quiet: opts.quiet?, verbose: opts.verbose?)
244
188
 
245
- # :init! is processed separately because the project object can't be
246
- # constructed before the directory has been initialized
189
+ # Handle init command
247
190
  if opts.subcommand == :init
248
- dir = args.expect(1)
249
- Prick::Project.initialize_directory(dir)
250
- Dir.chdir(dir) {
251
- Prick::Project.initialize_project(name)
252
- }
253
- if File.basename(dir) != name
254
- puts "Initialized project #{name} in #{dir}" if !opts.quiet?
255
- else
256
- puts "Initialized project #{name}" if !opts.quiet?
257
- end
258
- else
259
- project = Prick::Project.new(name)
260
-
261
- case cmd = opts.subcommand
191
+ directory = args.expect(0..1)
192
+ name = opts.name || (directory && File.basename(directory)) || File.basename(Dir.getwd)
193
+ user = opts.init!.user || name
194
+ program.init(name, user, directory || ".")
195
+ exit 0
196
+ end
262
197
 
263
- # INFO COMMANDS #########################################################
264
- #
265
- when :info
266
- args.expect(0)
198
+ # Check prick version
199
+ file = PrickVersion.new
200
+ file.exist? or raise Prick::Error, "Can't find prick version file '#{file.path}'"
201
+ VERSION == file.read.to_s or
202
+ raise Prick::Fail, ".prick-version required prick-#{file.read} but you're using prick-#{VERSION}"
203
+
204
+ # TODO: Check for dirty detached head
205
+
206
+ # Expect a sub-command
207
+ opts.subcommand or raise Prick::Error, "Subcomand expected"
208
+
209
+ case opts.subcommand
210
+ when :info
211
+ args.expect(0)
212
+ program.info
213
+
214
+ when :list
215
+ command = opts.list!
216
+ case command.subcommand
217
+ when :releases;
218
+ obj = command.releases!
219
+ program.list_releases(migrations: obj.migrations?, cancelled: obj.cancelled?)
220
+ when :migrations; program.list_migrations
221
+ when :upgrades; program.list_upgrades(*args.expect(0..2).compact)
222
+ when :cache;
223
+ args.expect(0)
224
+ program.list_cache
225
+ when NilClass; raise Prick::Error, "list requires a releases|migrations|upgrades sub-command"
226
+ else
227
+ raise Prick::Internal, "Subcommand #{opts.subcommand}.#{command.subcommand} is not matched"
228
+ end
267
229
 
268
- puts "Current branch"
269
- puts " #{project.release&.version || 'nil'}"
230
+ when :build
231
+ version = args.expect(0..1)
232
+ program.build(opts.build!.database, version, opts.build!.no_cache?)
270
233
 
271
- puts "Project database"
272
- db = project.database
273
- if db.exist?
274
- print " #{project.database.name}"
275
- print " (present)" if project.database.exist? && !project.database.loaded?
276
- print " (using #{project.database.version})" if project.database.loaded?
277
- else
278
- print " [missing]" if !project.database.exist?
279
- end
280
- puts
281
-
282
- puts "Releases"
283
- project.releases.each { |release|
284
- print " #{release}"
285
- if release.database.exist?
286
- if release.database.version
287
- print " loaded"
288
- else
289
- print " (empty database)"
290
- end
291
- end
292
- if release.cached?
293
- print ", cached"
294
- end
295
- print " <#{release.database.name}>"
296
- print "\n"
297
- }
298
- puts
299
- info(project)
300
-
301
- when :status
302
- args.expect(0)
303
- puts "On branch #{project.release}" + (project.dirty? ? " (dirty)" : "")
304
-
305
- when :history
306
- args.expect(0)
307
- history = project.release.history
308
- history.each { |release, features|
309
- puts release.to_s
310
- indent {
311
- features.map { |feature|
312
- puts "#{feature}, base release: #{feature.base_release}"
313
- }
314
- }
315
- }
316
-
317
-
318
- # GENERAL COMMANDS ######################################################
319
- #
320
- when :checkout
321
- version = args.expect(1)
322
- project.checkout(version)
323
- puts "Checked out #{version}"
324
-
325
- when :migrate
326
- project.migrate
327
-
328
-
329
- # DEVELOPER COMMANDS ####################################################
330
- #
331
- when :feature # Shorthand for 'create feature'
332
-
333
- when :rebase
334
-
335
- when :prepare
336
- args.expect(0)
337
- if object = opts.prepare.subcommand!
338
- case object
339
- when :migration
340
- project.prepare_migration
341
- puts "Prepared migration"
342
- else
343
- raise "Oops: subcommand #{opts.subcommand.inspect} not matched"
344
- end
345
- else
234
+ when :make
235
+ command = opts.make!
236
+ case command.subcommand
237
+ when :clean
346
238
  args.expect(0)
347
- project.prepare_release
348
- puts "Prepared new release" if !opts.quiet?
349
- end
350
-
351
-
352
- # RELEASE MANAGER COMMANDS ##############################################
353
- #
354
- when :create
355
- object = opts.create.subcommand!
356
- release =
357
- case object
358
- when :release
359
- version = compute_version(project, args.expect(0..1))
360
- project.create_release(version)
361
- when :prerelease
362
- version = compute_version(project, args.expect(1))
363
- project.create_prerelease(version)
364
- when :feature
365
- project.create_feature(args.expect(1))
366
- else
367
- raise "Oops: subcommand #{opts.subcommand.inspect} not matched"
368
- end
369
- puts "Created #{object} #{release.version}" if !opts.quiet?
370
-
371
- when :increment # and 'increment prerelease'
372
- args.expect(0)
373
- release = project.increment_prerelease
374
- puts "Created prerelease #{release.version}" if !opts.quiet?
375
-
376
- when :release # Shorthand for 'create release'
377
- version = compute_version(project, args.expect(0..1))
378
- release = project.create_release(version)
379
- puts "Created release #{release.version}" if !opts.quiet?
380
-
381
- when :prerelease # Shorthand for 'create prerelease' and 'increment'
382
- release =
383
- if project.release?
384
- version = compute_version(project, args.expect(1))
385
- project.create_prerelease(version)
386
- elsif project.prerelease?
387
- args.expect(0)
388
- project.increment_prerelease
389
- end
390
- puts "Created prerelease #{release.version}" if !opts.quiet?
391
-
392
- when :include
393
- name = args.expect(1)
394
- project.include_feature(name)
395
- puts "Included feature #{name}" if !opts.quiet?
396
- puts "Please check changed files and then commit using 'prick commit'"
397
-
398
- when :commit
399
- msg = project.commit_feature
400
- puts msg if !opts.quiet?
401
- puts "Committed changes" if !opts.quiet?
402
-
403
-
404
- # MANIPULATING DATABASES AND RELEASES ###################################
405
- #
406
- when :list
407
- # TODO: Mark cached releases as such
408
- args.expect(0)
409
- case opts.list.subcommand
410
- when :databases, nil; project.databases.map(&:name)
411
- when :releases; project.releases.map(&:name)
412
- when :prereleases; project.prereleases.map(&:version)
413
- else
414
- ShellOpts.error "Illegal list type: '#{opts.list.subcommand}"
415
- end.each { |e| puts e }
416
-
417
- when :build
418
- version = args.expect(0..1)
419
- project.build(version)
420
- if version
421
- puts "Built #{version}"
239
+ program.make_clean(command.clean!.all?)
422
240
  else
423
- puts "Built current version"
424
- end
425
-
426
- when :use
427
- file = opts.use.file
428
- version = args.expect(0..1)
429
- file || version or raise Error, "Need either a version or a -f FILE argument"
430
- file.nil? != version.nil? or raise Error, "Not both version and -f FILE arguments can be specified"
431
- file ||= project[version].archive.path
432
- project.load_database(nil, file)
433
-
434
- when :load
435
- file = opts.load.file
436
- version = args.expect(1)
437
- project.load_database(version, file)
438
-
439
- when :reload
440
- file = opts.reload.file
441
- version = args.expect(1)
442
- project.reload(version, file)
443
-
444
- when :unload
445
- version = args.expect(0..1)
446
- project.unload(version)
447
-
448
- when :clean
449
- version = args.expect(0..1)
450
- project.clean(version, project: true)
451
-
452
- when NilClass
453
- if args.size > 0
454
- ShellOpts.error "#{args.first} is not a command"
455
- end
456
- else
457
- raise "Oops: subcommand #{opts.subcommand.inspect} not matched"
458
- end
241
+ version = args.expect(0..1)
242
+ program.make(opts.make!.database, version, opts.make!.no_cache?)
243
+ end
244
+
245
+ when :load
246
+ version_or_file = args.expect(1)
247
+ program.load(opts.load!.database, version_or_file)
248
+
249
+ when :save
250
+ version, file = args.expect(1..2)
251
+ program.save(version, file)
252
+
253
+ when :drop
254
+ program.drop(args.expect(0..1), opts.drop!.all)
255
+
256
+ when :diff
257
+ mark = opts.diff!.mark
258
+ tables = opts.diff!.tables
259
+ no_tables = opts.diff!.notables
260
+ tables.nil? && no_tables.nil? || tables ^ no_tables or
261
+ raise Error, "--tables and --no-tables options are exclusive"
262
+ select = tables ? :tables : (no_tables ? :no_tables : :all)
263
+ from, to = args.expect(0..2)
264
+ program.diff(from, to, mark, select)
265
+
266
+ when :migrate
267
+ raise NotYet
268
+
269
+ when :prepare
270
+ cmd = opts.prepare!.subcommand || :release
271
+ case cmd
272
+ when :release; program.prepare_release(args.expect(0..1))
273
+ when :feature; program.prepare_feature(args.expect(1))
274
+ when :migration; program.prepare_migration(args.expect(0..1))
275
+ when :schema; program.prepare_schema(args.expect(1))
276
+ when :diff; program.prepare_diff(args.expect(0..1))
277
+ else
278
+ raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
279
+ end
280
+
281
+ when :include
282
+ cmd = opts.include!.subcommand || :feature
283
+ case cmd
284
+ when :feature; program.include_feature(args.expect(1))
285
+ else
286
+ raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
287
+ end
288
+
289
+ when :check
290
+ args.expect(0)
291
+ program.check
292
+
293
+ when :create
294
+ cmd = opts.create!.subcommand || :release
295
+ case cmd
296
+ when :release; program.create_release(args.expect(0..1))
297
+ when :prerelease; program.create_prerelease(args.expect(0..1))
298
+ when :feature; program.create_feature(args.expect(1))
299
+ else
300
+ raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
301
+ end
302
+
303
+ when :cancel
304
+ cmd = opts.cancel!.subcommand
305
+ case cmd
306
+ when :release; program.cancel_release(args.expect(1))
307
+ when nil; raise Prick::Error, "'cancel' subcommand requires a release argument"
308
+ else
309
+ raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
310
+ end
311
+
312
+ when :generate
313
+ cmd = opts.generate!.subcommand
314
+ case cmd
315
+ when :schema; program.generate_schema
316
+ when :migration; program.generate_migration
317
+ when nil; raise Prick::Error, "'generate' subcommand requires a 'schema' or 'migration' argument"
318
+ else
319
+ raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
320
+ end
321
+
322
+ when :upgrade
323
+ args.expect(0)
324
+ program.upgrade
325
+
326
+ when :backup
327
+ program.backup(args.expect(0..1))
328
+
329
+ when :restore
330
+ program.restore(args.expect(0..1))
331
+ else
332
+ raise Prick::Internal, "Subcommand #{opts.subcommand} is not matched"
459
333
  end
460
334
 
461
335
  rescue Prick::Fail => ex # Handling of Fail has to come first because Fail < Error
@@ -464,4 +338,56 @@ rescue Prick::Error => ex
464
338
  ShellOpts.error(ex.message)
465
339
  end
466
340
 
341
+ __END__
467
342
 
343
+ # Awaits support for sections in ShellOpts
344
+ HELP = %(
345
+ OPTIONS
346
+ -n, --name=NAME
347
+ -C, --directory=DIR
348
+ -h, --help
349
+ -v, --verbose
350
+ --version
351
+
352
+ COMMANDS
353
+ INITIALIZATION
354
+ init --user=USER [DIR]
355
+
356
+ INFO COMMANDS
357
+ info
358
+ list releases --migrations --cancelled
359
+ list migrations
360
+ list upgrades --all
361
+
362
+ BUILDING
363
+ build -d DATABASE -C --nocache [TAG]
364
+ make -d DATABASE -C --nocache [TAG]
365
+ make clean -a
366
+ load -d DATABASE VERSION|FILE
367
+ save VERSION [FILE]
368
+ drop --all [DATABASE]
369
+ diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]]
370
+ migrate
371
+
372
+ PREPARING RELEASES
373
+ prepare release [FORK]
374
+ prepare feature NAME
375
+ prepare migration FROM
376
+ prepare schema NAME
377
+ prepare diff [VERSION]
378
+ include feature FEATURE
379
+ check
380
+
381
+ CREATING RELEASES
382
+ create release [RELEASE]
383
+ create prerelease RELEASE
384
+ create feature NAME
385
+ cancel release RELEASE
386
+
387
+ DEPLOYING RELEASES
388
+ generate migration
389
+ generate schema
390
+ upgrade
391
+ backup [FILE]
392
+ restore [FILE]
393
+ )