prick 0.2.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.
- 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
|
+
|