prick 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/README.md +35 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/doc/create_release.txt +17 -0
- data/doc/flow.txt +98 -0
- data/doc/migra +1 -0
- data/doc/migrations.txt +172 -0
- data/doc/notes.txt +116 -0
- data/doc/sh.prick +316 -0
- data/exe/prick +467 -0
- data/file +0 -0
- data/lib/ext/algorithm.rb +14 -0
- data/lib/ext/fileutils.rb +8 -0
- data/lib/ext/pg.rb +18 -0
- data/lib/prick.rb +21 -0
- data/lib/prick/archive.rb +124 -0
- data/lib/prick/build.rb +376 -0
- data/lib/prick/command.rb +85 -0
- data/lib/prick/constants.rb +199 -0
- data/lib/prick/database.rb +58 -0
- data/lib/prick/dsort.rb +151 -0
- data/lib/prick/ensure.rb +119 -0
- data/lib/prick/exceptions.rb +13 -0
- data/lib/prick/git.rb +159 -0
- data/lib/prick/migra.rb +22 -0
- data/lib/prick/migration.rb +230 -0
- data/lib/prick/project.rb +444 -0
- data/lib/prick/rdbms.rb +147 -0
- data/lib/prick/schema.rb +100 -0
- data/lib/prick/version.rb +133 -0
- data/make_releases +369 -0
- data/prick.gemspec +46 -0
- data/share/features/diff.sql +2 -0
- data/share/features/feature/diff.sql +2 -0
- data/share/features/feature/migrate.sql +2 -0
- data/share/features/features.sql +2 -0
- data/share/features/features.yml +2 -0
- data/share/features/migrations.sql +4 -0
- data/share/gitignore +2 -0
- data/share/schemas/prick/data.sql +8 -0
- data/share/schemas/prick/schema.sql +20 -0
- metadata +188 -0
data/doc/sh.prick
ADDED
@@ -0,0 +1,316 @@
|
|
1
|
+
#!/usr/bin/bash
|
2
|
+
|
3
|
+
# Name
|
4
|
+
# prick - Database project tool
|
5
|
+
#
|
6
|
+
# Usage
|
7
|
+
# prick prepare|migrate|release
|
8
|
+
#
|
9
|
+
# Commands
|
10
|
+
# prepare NEW-RELEASE
|
11
|
+
# Stamps NEW-RELEASE into the database
|
12
|
+
# migrate [OLD-RELEASE]
|
13
|
+
# Create a migration from OLD-RELEASE to the prepared release.
|
14
|
+
# OLD-RELEASE defaults to the last registered release
|
15
|
+
# release
|
16
|
+
# Release the prepared release
|
17
|
+
#
|
18
|
+
# Files
|
19
|
+
# prick use the following sub-directories:
|
20
|
+
#
|
21
|
+
# releases/
|
22
|
+
# <database>-<release>.dump.gz
|
23
|
+
# ...
|
24
|
+
# migrations/
|
25
|
+
# migrate-<from>-<to>[.sql]
|
26
|
+
# ...
|
27
|
+
#
|
28
|
+
# The releases directory contains schema backups that makes it easy to
|
29
|
+
# restore a earlier release used to generated migrations. The migrations directory
|
30
|
+
# contains migrations from one release to another. 'prick migrate' generates
|
31
|
+
# a SQL migration file but you can replace it with an executable if needed
|
32
|
+
#
|
33
|
+
|
34
|
+
# TODO: Schema-only dump of database
|
35
|
+
# pg_dump --no-owner --no-privileges --schema-only name_of_database -f schema.dump.sql
|
36
|
+
|
37
|
+
|
38
|
+
set -e
|
39
|
+
|
40
|
+
. bash.include
|
41
|
+
|
42
|
+
USAGE="prepare NEW-RELEASE
|
43
|
+
migrate OLD-RELEASE
|
44
|
+
release"
|
45
|
+
|
46
|
+
DB=mikras
|
47
|
+
|
48
|
+
function inoa() {
|
49
|
+
error "Illegal number of arguments"
|
50
|
+
}
|
51
|
+
|
52
|
+
[ $# -ge 1 ] || inoa
|
53
|
+
CMD=$1
|
54
|
+
shift
|
55
|
+
|
56
|
+
trap 'eval rm -rf $ERRFILE $TMPFILE $TMPDIR' EXIT
|
57
|
+
ERRFILE=$(mktemp --tmpdir $PROGRAM.XXXXXXXXXX)
|
58
|
+
TMPFILE=$(mktemp --tmpdir $PROGRAM.XXXXXXXXXX)
|
59
|
+
TMPDIR=$(mktemp --tmpdir --directory $PROGRAM.XXXXXXXXXX)
|
60
|
+
#echo $TMPFILE
|
61
|
+
#echo $TMPDIR
|
62
|
+
|
63
|
+
function last_release() {
|
64
|
+
local path=$(ls releases/$DB-*.*.*.dump.gz 2>/dev/null | tail -1)
|
65
|
+
local release=$(echo $path | sed -e "s/releases\/$DB-//" -e 's/\.dump\.gz$//')
|
66
|
+
echo ${release:-0.0.0}
|
67
|
+
}
|
68
|
+
|
69
|
+
function current_release() {
|
70
|
+
psql -qtAX -d $DB -c "select major || '.' || minor || '.' || patch from meta.versions"
|
71
|
+
}
|
72
|
+
|
73
|
+
function set_current_release() {
|
74
|
+
set -- $(echo $1 | sed 's/\./ /g')
|
75
|
+
local major=$1
|
76
|
+
local minor=$2
|
77
|
+
local patch=$3
|
78
|
+
|
79
|
+
psql -qtAX -d $DB -c "update meta.versions set major = $major, minor = $minor, patch = $patch"
|
80
|
+
}
|
81
|
+
|
82
|
+
function has_release() {
|
83
|
+
local release=$1
|
84
|
+
local db=$DB-$release
|
85
|
+
psql -qtAX -d $DB -l | grep -q "^$db|" &>/dev/null
|
86
|
+
}
|
87
|
+
|
88
|
+
function database_load() {
|
89
|
+
local release=$1
|
90
|
+
local db=$DB-$release
|
91
|
+
local dump=releases/$db.dump.gz
|
92
|
+
[ -f $dump ] || error "Can't find release dump $dump"
|
93
|
+
[ ! has_release ] || error "Release $release is already loaded"
|
94
|
+
createdb $db
|
95
|
+
gzip --to-stdout $dump | psql -d $db
|
96
|
+
}
|
97
|
+
|
98
|
+
function redatabase_load() {
|
99
|
+
local release=$1
|
100
|
+
local db=$DB-$release
|
101
|
+
local dump=releases/$db.dump.gz
|
102
|
+
[ -f $dump ] || error "Can't find release dump $dump"
|
103
|
+
[ ! has_release ] || dropdb $db
|
104
|
+
createdb $db
|
105
|
+
gzip --to-stdout $dump | psql -d $db
|
106
|
+
}
|
107
|
+
|
108
|
+
|
109
|
+
function prepare() {
|
110
|
+
local new_release=$1
|
111
|
+
mesg "Preparing $new_release"
|
112
|
+
|
113
|
+
[ ! -f $new_release ] || error "New release already exists: $new_release_dump"
|
114
|
+
set_current_release $new_release
|
115
|
+
}
|
116
|
+
|
117
|
+
function migrate() {
|
118
|
+
local old_release=$1
|
119
|
+
local new_release=$(current_release)
|
120
|
+
local old_release_dump=releases/$DB-$old_release.dump.gz
|
121
|
+
local new_release_dump=releases/$DB-$new_release_dump.gz
|
122
|
+
local migration=migrations/migrate-$old_release-$new_release.sql
|
123
|
+
|
124
|
+
mesg "Migrating $old_release to $new_release"
|
125
|
+
|
126
|
+
[ -f $old_release_dump ] || error "Can't find old release: $old_release_dump"
|
127
|
+
[ ! -f $new_release_dump ] || error "New release already exists: $new_release_dump"
|
128
|
+
[ "$old_release" != "$new_release" ] ||
|
129
|
+
error "Can't migrate $old_release to itself (did you forget to prepare the release?)"
|
130
|
+
[ ! -f $migration ] || error "Won't overwrite migration $migration"
|
131
|
+
|
132
|
+
has_release $old_release || database_load $old_release
|
133
|
+
# migra --unsafe postgresql:///$DB-$old_release postgresql:///$DB || true #>$migration
|
134
|
+
migra --unsafe postgresql:///$DB-$old_release postgresql:///$DB >$migration || {
|
135
|
+
case $? in
|
136
|
+
0)
|
137
|
+
mesg "No changes"
|
138
|
+
;;
|
139
|
+
2)
|
140
|
+
mesg "Generated $migration"
|
141
|
+
;;
|
142
|
+
*)
|
143
|
+
rm -f $migration
|
144
|
+
fail "migra failed"
|
145
|
+
;;
|
146
|
+
esac
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
function database() {
|
151
|
+
local release=$1
|
152
|
+
echo "$DB-$release"
|
153
|
+
}
|
154
|
+
|
155
|
+
function dumpfile() {
|
156
|
+
local release=$1
|
157
|
+
echo "releases/$DB-$release.dump.gz"
|
158
|
+
}
|
159
|
+
|
160
|
+
###
|
161
|
+
### R E L E A S E
|
162
|
+
###
|
163
|
+
|
164
|
+
function release_default() {
|
165
|
+
psql -qtAX -d $DB -c "select major || '.' || minor || '.' || patch from meta.versions"
|
166
|
+
}
|
167
|
+
|
168
|
+
function release_exist() {
|
169
|
+
local release=$1
|
170
|
+
local dump=$(dumpfile $release)
|
171
|
+
[ -f $dump ]
|
172
|
+
}
|
173
|
+
|
174
|
+
###
|
175
|
+
### M I G R A T I O N S
|
176
|
+
###
|
177
|
+
|
178
|
+
function migration_create() {
|
179
|
+
local old_release=$1
|
180
|
+
local new_release=$(release_default)
|
181
|
+
}
|
182
|
+
|
183
|
+
function migration_apply() {
|
184
|
+
echo niy
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
###
|
189
|
+
### D A T A B A S E
|
190
|
+
###
|
191
|
+
|
192
|
+
function database_loaded() {
|
193
|
+
local release=$1
|
194
|
+
local db=$(database $release)
|
195
|
+
psql -qtAX -d $DB -l | grep -q "^$db|" &>/dev/null
|
196
|
+
}
|
197
|
+
|
198
|
+
function database_build() {
|
199
|
+
local release=$1
|
200
|
+
local db=$(database $release)
|
201
|
+
! database_loaded $release || fail "Database $db is already loaded"
|
202
|
+
mesg "Checking out release $release"
|
203
|
+
git archive --format=tar $release >$TMPFILE
|
204
|
+
(
|
205
|
+
cd $TMPDIR
|
206
|
+
tar xf $TMPFILE
|
207
|
+
|
208
|
+
# No git access so set spec.files to []
|
209
|
+
sed -i -e '/^ *spec.files/,+2d' mikras_db.gemspec -e 's/spec.files/[]/' mikras_db.gemspec
|
210
|
+
|
211
|
+
mesg "Running bundle"
|
212
|
+
bundle &>$ERRFILE || {
|
213
|
+
cat $ERRFILE >&2
|
214
|
+
fail "bundle failed"
|
215
|
+
}
|
216
|
+
|
217
|
+
# Set databse name
|
218
|
+
sed -i "s/^DB=.*/DB=$(database $release)/" exe/build
|
219
|
+
|
220
|
+
# Quote variables properly
|
221
|
+
perl -pi -e 's/:(\w+)/:"\1"/g' schemas/db.sql
|
222
|
+
|
223
|
+
# Replace schemas/skuffejern/users.sql - it is not possible to drop users because we
|
224
|
+
# run several versions of the database
|
225
|
+
for file in $(ls schemas/*/users.sql 2>/dev/null); do
|
226
|
+
{
|
227
|
+
psql -qtAX -d template1 \
|
228
|
+
-c "select 1 from pg_user where pg_user.usename = 'anonymous'" \
|
229
|
+
| grep -q 1 || psql -qtAX -d template1 -c "create user anonymous"
|
230
|
+
|
231
|
+
psql -qtAX -d template1 \
|
232
|
+
-c "select 1 from pg_roles where pg_roles.rolname = 'skuffejern_access'" \
|
233
|
+
| grep -q 1 || psql -qtAX -d template1 -c "create role skuffejern_access"
|
234
|
+
} >$file
|
235
|
+
done
|
236
|
+
|
237
|
+
mesg "Running rebuild"
|
238
|
+
./rebuild &>$ERRFILE || {
|
239
|
+
cat $ERRFILE >&2
|
240
|
+
fail "rebuild failed"
|
241
|
+
}
|
242
|
+
)
|
243
|
+
}
|
244
|
+
|
245
|
+
function database_save() {
|
246
|
+
local release=$1
|
247
|
+
local db=$(database $release)
|
248
|
+
local dump=releases/$db.dump.gz
|
249
|
+
[ ! -f $dump ] || fail "Won't overwrite release file $dump"
|
250
|
+
# pg_dump --no-owner --no-privileges --schema-only name_of_database -f schema.dump.sql
|
251
|
+
pg_dump --no-owner --schema-only $db | gzip --to-stdout >$dump
|
252
|
+
}
|
253
|
+
|
254
|
+
function database_load() {
|
255
|
+
local release=$1
|
256
|
+
local db=$(database $release)
|
257
|
+
local dump=releases/$db.dump.gz
|
258
|
+
! database_loaded $db || fail "Database $db is already loaded"
|
259
|
+
[ -f $dump ] || fail "Can't find release file $dump"
|
260
|
+
createdb $db
|
261
|
+
gunzip --to-stdout $dump | psql -d $db &>$ERRFILE || {
|
262
|
+
cat $ERRFILE
|
263
|
+
fail "psql failed"
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
function database_drop() {
|
268
|
+
local release=$1
|
269
|
+
local db=$(database $release)
|
270
|
+
dropdb $db
|
271
|
+
}
|
272
|
+
|
273
|
+
|
274
|
+
case $CMD in
|
275
|
+
list)
|
276
|
+
[ $# = 0 ] || inoa
|
277
|
+
psql -qtAX -d template1 -l | sed -n "/mikras[-0-9.]*|/s/|.*//p"
|
278
|
+
;;
|
279
|
+
build)
|
280
|
+
[ $# = 1 ] || inoa
|
281
|
+
database_build $1
|
282
|
+
;;
|
283
|
+
save)
|
284
|
+
[ $# = 1 ] || inoa
|
285
|
+
database_save $1
|
286
|
+
;;
|
287
|
+
load)
|
288
|
+
[ $# = 1 ] || inoa
|
289
|
+
database_load $1
|
290
|
+
;;
|
291
|
+
reload)
|
292
|
+
[ $# = 1 ] || inoa
|
293
|
+
! database_loaded $1 || database_drop $1
|
294
|
+
database_load $1
|
295
|
+
;;
|
296
|
+
drop)
|
297
|
+
[ $# = 1 ] || inoa
|
298
|
+
database_drop $1
|
299
|
+
;;
|
300
|
+
prepare)
|
301
|
+
[ $# = 1 ] || inoa
|
302
|
+
prepare $1
|
303
|
+
;;
|
304
|
+
migrate) # TODO: prick migrate from-release to-release
|
305
|
+
[ $# -le 1 ] || inoa
|
306
|
+
migrate ${1:-$(last_release)}
|
307
|
+
;;
|
308
|
+
release)
|
309
|
+
mesg "Releasing"
|
310
|
+
;;
|
311
|
+
*)
|
312
|
+
error "Illegal command: '$CMD'"
|
313
|
+
;;
|
314
|
+
esac
|
315
|
+
|
316
|
+
|
data/exe/prick
ADDED
@@ -0,0 +1,467 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'prick.rb'
|
4
|
+
|
5
|
+
require 'shellopts'
|
6
|
+
require 'indented_io'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'ext/algorithm.rb'
|
9
|
+
|
10
|
+
include ShellOpts
|
11
|
+
|
12
|
+
# TODO: Backup & restore
|
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
|
62
|
+
Name of project. Defauls to the environment variable `PRICK_PROJECT` if
|
63
|
+
set and else the name of the current directory
|
64
|
+
|
65
|
+
-C, --directory=DIR
|
66
|
+
Change to directory DIR before anything else
|
67
|
+
|
68
|
+
-h, --help
|
69
|
+
Print this page
|
70
|
+
|
71
|
+
-v, --verbose
|
72
|
+
Be verbose
|
73
|
+
|
74
|
+
--version
|
75
|
+
Print prick version
|
76
|
+
|
77
|
+
COMMANDS
|
78
|
+
INFO COMMANDS
|
79
|
+
init [DIR]
|
80
|
+
Initialize a project directory
|
81
|
+
|
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
|
122
|
+
|
123
|
+
(need some thought)
|
124
|
+
|
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
|
130
|
+
|
131
|
+
include FEATURE
|
132
|
+
Merge a feature branch into the prepared release
|
133
|
+
|
134
|
+
prerelease
|
135
|
+
Bumps the pre-release serial and create a new branch
|
136
|
+
|
137
|
+
release [VERSION]
|
138
|
+
Create a new release. The version is not used if the release was prepared
|
139
|
+
|
140
|
+
|
141
|
+
MANIPULATING DATABASES AND RELEASES
|
142
|
+
list [databases|releases|prereleases]
|
143
|
+
Output a list of databases, releases, or prereleases. List databases by
|
144
|
+
default
|
145
|
+
|
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
|
150
|
+
|
151
|
+
load
|
152
|
+
Load the current cache into the project database
|
153
|
+
|
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
|
160
|
+
|
161
|
+
unload [VERSION]
|
162
|
+
Delete the versioned database. Default is to delete all project related
|
163
|
+
databases
|
164
|
+
|
165
|
+
clean [VERSION]
|
166
|
+
Delete the cached release. Default is to delete all cached releases
|
167
|
+
|
168
|
+
|
169
|
+
)
|
170
|
+
|
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
|
+
|
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
|
+
|
211
|
+
opts, args = ShellOpts.as_struct(SPEC, ARGV)
|
212
|
+
|
213
|
+
# Handle --help
|
214
|
+
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
|
223
|
+
exit
|
224
|
+
end
|
225
|
+
|
226
|
+
# Handle --version
|
227
|
+
if opts.version?
|
228
|
+
puts "prick-#{Prick::VERSION}"
|
229
|
+
exit
|
230
|
+
end
|
231
|
+
|
232
|
+
begin
|
233
|
+
# 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}'"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
name = opts.name || ENV["PRICK_PROJECT"] || File.basename(Dir.getwd)
|
243
|
+
verbose = opts.verbose?
|
244
|
+
|
245
|
+
# :init! is processed separately because the project object can't be
|
246
|
+
# constructed before the directory has been initialized
|
247
|
+
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
|
262
|
+
|
263
|
+
# INFO COMMANDS #########################################################
|
264
|
+
#
|
265
|
+
when :info
|
266
|
+
args.expect(0)
|
267
|
+
|
268
|
+
puts "Current branch"
|
269
|
+
puts " #{project.release&.version || 'nil'}"
|
270
|
+
|
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
|
346
|
+
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}"
|
422
|
+
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
|
459
|
+
end
|
460
|
+
|
461
|
+
rescue Prick::Fail => ex # Handling of Fail has to come first because Fail < Error
|
462
|
+
ShellOpts.fail(ex.message)
|
463
|
+
rescue Prick::Error => ex
|
464
|
+
ShellOpts.error(ex.message)
|
465
|
+
end
|
466
|
+
|
467
|
+
|