prick 0.18.0 → 0.19.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 +4 -4
- data/exe/prick +28 -10
- data/lib/prick/branch.rb +11 -0
- data/lib/prick/builder.rb +67 -26
- data/lib/prick/command.rb +3 -1
- data/lib/prick/database.rb +3 -3
- data/lib/prick/head.rb +6 -0
- data/lib/prick/program.rb +19 -238
- data/lib/prick/project.rb +3 -3
- data/lib/prick/version.rb +2 -2
- data/lib/prick.rb +2 -0
- data/prick.gemspec +16 -0
- metadata +72 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75523a625d7139830b419f2624f96af3c029dc1ad89b0cb40dec289f4b94b522
|
4
|
+
data.tar.gz: 450c53de9ef6b3c8627df7471076b87c559f367d7aef30e009a846bffa98390c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98b24c53c2ae54007c46cb4157963ba049327e6ea9b73c5b6b8502367e58d9d018acdb74c9896394bd90a0335359719c0e02523f1e2965de1ed31c023285eeb9
|
7
|
+
data.tar.gz: 25b7425c7f8547bdd0be1ee0a570b1b6115f08ff2ba3bd8c8284cced0ffc5da1ecbc0670e57bb21d2b9a45276df03d0f2f3210b83712ff50f55f811991ba11e3
|
data/exe/prick
CHANGED
@@ -10,6 +10,7 @@ require 'tempfile'
|
|
10
10
|
include ShellOpts
|
11
11
|
include Prick
|
12
12
|
|
13
|
+
|
13
14
|
SPEC = %(
|
14
15
|
-n,name=NAME
|
15
16
|
Name of project. Defauls to the environment variable `PRICK_PROJECT` if
|
@@ -51,11 +52,13 @@ SPEC = %(
|
|
51
52
|
list.cache!
|
52
53
|
List cache files
|
53
54
|
|
54
|
-
build! -d,database=DATABASE -C,no-cache [TAG]
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
option overrides the
|
55
|
+
build! -d,database=DATABASE -s,state=FILE -C,no-cache [TAG]
|
56
|
+
Drop all users associated with the database before building the current
|
57
|
+
database from the content in the schemas/ directory. With a tag the
|
58
|
+
version is built into the associated versioned database and the result is
|
59
|
+
saved to cache unless the -C option is given. The -d option overrides the
|
60
|
+
default database and the -s option overrides the default state file
|
61
|
+
(fox.state)
|
59
62
|
|
60
63
|
make! -d,database=DATABASE -C,no-cache [TAG]
|
61
64
|
Build the current database from the content in the schemas/ directory.
|
@@ -77,8 +80,12 @@ SPEC = %(
|
|
77
80
|
given file
|
78
81
|
|
79
82
|
drop! -a,all [DATABASE]
|
80
|
-
Drop the given database or all versioned databases.
|
81
|
-
|
83
|
+
Drop the given database or all versioned databases. Users with a username
|
84
|
+
on the form <database>__<username> are also dropped. The --all option
|
85
|
+
also drops the project database
|
86
|
+
|
87
|
+
drop.users! [DATABASE]
|
88
|
+
Drop users with a username on the form <database>__<username>
|
82
89
|
|
83
90
|
diff! -m,mark -t,tables -T,notables
|
84
91
|
diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]]
|
@@ -153,7 +160,7 @@ SPEC = %(
|
|
153
160
|
in the var/spool directory
|
154
161
|
)
|
155
162
|
|
156
|
-
|
163
|
+
DEFAULT_STATE_FILE = "fox.state"
|
157
164
|
|
158
165
|
opts, args = ShellOpts.process(SPEC, ARGV)
|
159
166
|
|
@@ -185,6 +192,7 @@ begin
|
|
185
192
|
|
186
193
|
# Create program object
|
187
194
|
program = Program.new(quiet: opts.quiet?, verbose: opts.verbose?)
|
195
|
+
$verbose = opts.verbose? ? opts.verbose : nil
|
188
196
|
|
189
197
|
# Handle init command
|
190
198
|
if opts.subcommand == :init
|
@@ -236,7 +244,9 @@ begin
|
|
236
244
|
|
237
245
|
when :build
|
238
246
|
version = args.expect(0..1)
|
239
|
-
|
247
|
+
state_file = File.expand_path(opts.build!.state || DEFAULT_STATE_FILE)
|
248
|
+
FileUtils.rm_f(state_file)
|
249
|
+
program.build(opts.build!.database, version, state_file, opts.build!.no_cache?)
|
240
250
|
|
241
251
|
when :make
|
242
252
|
command = opts.make!
|
@@ -258,7 +268,15 @@ begin
|
|
258
268
|
program.save(version, file)
|
259
269
|
|
260
270
|
when :drop
|
261
|
-
|
271
|
+
command = opts.drop!
|
272
|
+
case command.subcommand
|
273
|
+
when :users
|
274
|
+
database = args.extract(0..1) || program.project.database.name
|
275
|
+
args.expect(0)
|
276
|
+
program.drop_users(database)
|
277
|
+
else
|
278
|
+
program.drop(args.expect(0..1), opts.drop!.all?)
|
279
|
+
end
|
262
280
|
|
263
281
|
when :diff
|
264
282
|
mark = opts.diff!.mark
|
data/lib/prick/branch.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require "prick/state.rb"
|
2
2
|
|
3
|
+
# FIXME FIXME FIXME Not used!
|
4
|
+
|
5
|
+
|
3
6
|
module Prick
|
4
7
|
class Branch
|
5
8
|
# Branch name. It is usually equal to the version but migrations use a
|
@@ -116,6 +119,14 @@ module Prick
|
|
116
119
|
end
|
117
120
|
end
|
118
121
|
|
122
|
+
# A user defined branch. User defined branches have no version and only the
|
123
|
+
# #build method is defined TODO: Make all methods but #build private
|
124
|
+
class UserBranch < Branch
|
125
|
+
def initialize(name)
|
126
|
+
super(name, nil, nil, nil, nil)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
119
130
|
class AbstractRelease < Branch
|
120
131
|
def create(schema_version = self.version)
|
121
132
|
super()
|
data/lib/prick/builder.rb
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
require "prick/state.rb"
|
2
2
|
|
3
|
+
require 'indented_io'
|
4
|
+
|
3
5
|
module Prick
|
4
6
|
# Builder is a procedural object for building schemas and executing
|
5
7
|
# migrations. Builder use resources that can be executables, SQL files, YAML
|
6
|
-
# files, or directories. A resource is identified by a name (eg.
|
7
|
-
# and is used to match a build file. Build files are looked up
|
8
|
-
# following order:
|
8
|
+
# files, FOX files, or directories. A resource is identified by a name (eg.
|
9
|
+
# 'my-tables') and is used to match a build file. Build files are looked up
|
10
|
+
# in the following order:
|
9
11
|
#
|
10
12
|
# #{name} executable
|
11
13
|
# #{name}.* executable
|
12
14
|
# #{name}.yml
|
13
15
|
# #{name}.sql
|
16
|
+
# #{name}.fox
|
14
17
|
# #{name}/
|
15
18
|
#
|
16
19
|
# The output from executable objects is expected to be SQL statements that
|
17
|
-
# are fed into postgres
|
20
|
+
# are then fed into postgres
|
18
21
|
#
|
19
22
|
# When a resource match a directory, the directory can contain a special
|
20
23
|
# default resource ('build' or 'migrate') that takes over the rest of the
|
@@ -23,8 +26,8 @@ module Prick
|
|
23
26
|
# directory doesn't contain a build resource, the resources in the directory
|
24
27
|
# are built in alphabetic order
|
25
28
|
#
|
26
|
-
# Build
|
27
|
-
#
|
29
|
+
# Build SQL scripts and executables are executed with search path set to
|
30
|
+
# containing schema. This doesn't include migration script or executable
|
28
31
|
#
|
29
32
|
class Builder
|
30
33
|
attr_reader :database
|
@@ -32,6 +35,11 @@ module Prick
|
|
32
35
|
attr_reader :default # either "build" or "migrate"
|
33
36
|
attr_reader :lines
|
34
37
|
|
38
|
+
def state_file() self.class.state_file end
|
39
|
+
def state_file=(file) self.class.state_file = file end
|
40
|
+
def self.state_file() @@state_file end
|
41
|
+
def self.state_file=(file) @@state_file = file end
|
42
|
+
|
35
43
|
def initialize(database, directory, default)
|
36
44
|
@database = database
|
37
45
|
@directory = directory
|
@@ -42,9 +50,9 @@ module Prick
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def build(subject = default, execute: true)
|
53
|
+
puts "build(#{subject.inspect}, execute: #{execute.inspect})" if $verbose > 0
|
45
54
|
@execute = execute
|
46
55
|
@lines = []
|
47
|
-
# puts "Building #{subject.inspect}"
|
48
56
|
Dir.chdir(directory) {
|
49
57
|
if subject
|
50
58
|
build_subject(subject)
|
@@ -59,15 +67,23 @@ module Prick
|
|
59
67
|
def self.yml_file(directory) raise NotThis end
|
60
68
|
|
61
69
|
protected
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
70
|
+
def do_path(path, &block)
|
71
|
+
puts "do_path(#{path.inspect})" if $verbose >= 3
|
72
|
+
if File.directory?(path)
|
73
|
+
dir, file = path, nil
|
74
|
+
else
|
75
|
+
dir, file = File.split(path)
|
76
|
+
end
|
77
|
+
if $verbose >= 2
|
78
|
+
indent { Dir.chdir(dir) { yield(file) } }
|
79
|
+
else
|
80
|
+
Dir.chdir(dir) { yield(file) }
|
81
|
+
end
|
66
82
|
end
|
67
83
|
|
68
84
|
def do_sql(path, schema: nil)
|
69
|
-
|
70
|
-
|
85
|
+
puts "do_sql(#{path})" if $verbose >= 2
|
86
|
+
do_path(path) { |file|
|
71
87
|
if @execute
|
72
88
|
begin
|
73
89
|
Rdbms.exec_file(database.name, file, user: database.user, schema: schema)
|
@@ -83,11 +99,29 @@ module Prick
|
|
83
99
|
true
|
84
100
|
end
|
85
101
|
|
102
|
+
def do_fox(path, schema: nil)
|
103
|
+
puts "do_fox(#{path.inspect}, #{schema.inspect})" if $verbose >= 2
|
104
|
+
do_path(path) { |file|
|
105
|
+
lines = Command.command "fox --state=#{state_file} --write --delete=none #{database.name} #{file}"
|
106
|
+
if @execute
|
107
|
+
begin
|
108
|
+
Rdbms.exec_sql(database.name, lines.join("\n"), user: database.user, schema: schema)
|
109
|
+
rescue Command::Error => ex
|
110
|
+
$stderr.puts ex.stderr
|
111
|
+
$stderr.puts "from #{reldir}/#{file}"
|
112
|
+
exit 1
|
113
|
+
end
|
114
|
+
else
|
115
|
+
@lines += lines
|
116
|
+
end
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
86
120
|
def do_exe(path, args = [], schema: nil)
|
87
|
-
|
88
|
-
|
121
|
+
puts "do_exe(#{path.inspect}, #{args.inspect})" if $verbose >= 2
|
122
|
+
do_path(path) { |file|
|
89
123
|
cmd = (["./#{file}"] + args).join(" ")
|
90
|
-
lines = Command.command cmd, stdin: [database.name, database.user]
|
124
|
+
lines = Command.command cmd, stdin: [database.name, database.user], stderr: nil
|
91
125
|
if @execute
|
92
126
|
begin
|
93
127
|
Rdbms.exec_sql(database.name, lines.join("\n"), user: database.user, schema: schema)
|
@@ -104,14 +138,16 @@ module Prick
|
|
104
138
|
end
|
105
139
|
|
106
140
|
def do_yml(path)
|
107
|
-
|
108
|
-
|
109
|
-
|
141
|
+
puts "do_yml(#{path})" if $verbose >= 2
|
142
|
+
do_path(path) { |file|
|
143
|
+
YAML.load(File.read(file))&.each { |subject| build_subject(subject) }
|
144
|
+
}
|
110
145
|
true
|
111
146
|
end
|
112
147
|
|
113
|
-
# A subject can be both an abstract subject or a concrete file (*.yml, *.sql)
|
148
|
+
# A subject can be both an abstract subject or a concrete file (*.yml, *.sql, or *.fox)
|
114
149
|
def build_subject(subject)
|
150
|
+
puts "build_subject(#{subject.inspect}) in #{Dir.getwd}" if $verbose > 0
|
115
151
|
cmd, *args = subject.split(/\s+/)
|
116
152
|
if File.file?(cmd) && File.executable?(cmd)
|
117
153
|
do_exe(cmd, args)
|
@@ -119,6 +155,8 @@ module Prick
|
|
119
155
|
do_yml(subject)
|
120
156
|
elsif File.file?(subject) && subject.end_with?(".sql")
|
121
157
|
do_sql(subject)
|
158
|
+
elsif File.file?(subject) && subject.end_with?(".fox")
|
159
|
+
do_fox(subject)
|
122
160
|
elsif File.exist?(yml_file = "#{subject}.yml")
|
123
161
|
do_yml(yml_file)
|
124
162
|
elsif File.exist?(sql_file = "#{subject}.sql")
|
@@ -131,16 +169,19 @@ module Prick
|
|
131
169
|
end
|
132
170
|
|
133
171
|
def build_directory(path)
|
134
|
-
|
135
|
-
|
136
|
-
|
172
|
+
puts "build_directory(#{path.inspect}) in #{Dir.getwd}" if $verbose >= 2
|
173
|
+
do_path(path) { |_|
|
174
|
+
build_subject(@default) || begin
|
175
|
+
if !Dir["#{default}", "#{default}.yml", "#{default}.sql"].empty?
|
137
176
|
subjects = [default]
|
138
177
|
else
|
178
|
+
candidates = Dir["*"]
|
139
179
|
exes = candidates.select { |file| File.file?(file) && File.executable?(file) }
|
140
180
|
ymls = candidates.select { |file| file.end_with?(".yml") }.map { |f| f.sub(/\.yml$/, "") }
|
141
181
|
sqls = candidates.select { |file| file.end_with?(".sql") }.map { |f| f.sub(/\.sql$/, "") }
|
182
|
+
foxs = candidates.select { |file| file.end_with?(".fox") }.map { |f| f.sub(/\.fox$/, "") }
|
142
183
|
dirs = candidates.select { |file| File.directory?(file) }
|
143
|
-
subjects = (exes + ymls + sqls + dirs).uniq.sort
|
184
|
+
subjects = (exes + ymls + sqls + foxs + dirs).uniq.sort #.reject { |f| f != "diff" } FIXME ??
|
144
185
|
end
|
145
186
|
subjects.inject(false) { |a, s| build_subject(s) || a }
|
146
187
|
end
|
@@ -192,12 +233,12 @@ module Prick
|
|
192
233
|
|
193
234
|
protected
|
194
235
|
def do_sql(path)
|
195
|
-
schema = Dir.getwd
|
236
|
+
schema = Dir.getwd =~ /^.*schema\/([^\/]+)(?:\/.*)?$/ ? $1 : "public"
|
196
237
|
super(path, schema: schema)
|
197
238
|
end
|
198
239
|
|
199
240
|
def do_exe(path, args = [])
|
200
|
-
schema = Dir.getwd
|
241
|
+
schema = Dir.getwd =~ /^.*schema\/([^\/]+)(?:\/.*)?$/ ? $1 : "public"
|
201
242
|
super(path, args, schema: schema)
|
202
243
|
end
|
203
244
|
end
|
data/lib/prick/command.rb
CHANGED
data/lib/prick/database.rb
CHANGED
@@ -61,11 +61,11 @@ module Prick
|
|
61
61
|
|
62
62
|
# Hollow-out a database without dropping it. This is useful compared to a
|
63
63
|
# simple drop database since it wont block on other sessions wont block
|
64
|
-
def clean()
|
64
|
+
def clean()
|
65
65
|
if exist?
|
66
66
|
schemas = Rdbms.select_values(
|
67
|
-
|
68
|
-
"select nspname from pg_namespace where nspowner != 10")
|
67
|
+
name,
|
68
|
+
"select nspname from pg_namespace where nspowner != 10 and nspname != 'prick'")
|
69
69
|
for schema in schemas
|
70
70
|
Rdbms.exec_sql(name, "drop schema \"#{schema}\" cascade")
|
71
71
|
end
|
data/lib/prick/head.rb
CHANGED
data/lib/prick/program.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
|
2
|
+
require "pg_conn"
|
2
3
|
require "prick.rb"
|
3
4
|
|
4
5
|
module Prick
|
@@ -64,12 +65,14 @@ module Prick
|
|
64
65
|
project.cache.list.each { |l| puts l }
|
65
66
|
end
|
66
67
|
|
67
|
-
def build(database, version, nocache)
|
68
|
+
def build(database, version, state_file, nocache)
|
68
69
|
into_mesg = database && "into #{database}"
|
69
70
|
version_mesg = version ? "v#{version}" : "current schema"
|
70
71
|
version &&= Version.new(version)
|
71
72
|
version.nil? || Git.tag?(version) or raise Error, "Can't find tag v#{version}"
|
72
73
|
database = database ? Database.new(database, project.user) : project.database(version)
|
74
|
+
drop_users(database.name)
|
75
|
+
Builder.state_file = state_file
|
73
76
|
project.build(database, version: version)
|
74
77
|
project.save(database) if version && !nocache
|
75
78
|
mesg "Built", version_mesg, into_mesg
|
@@ -113,6 +116,7 @@ module Prick
|
|
113
116
|
|
114
117
|
def drop(database, all)
|
115
118
|
database.nil? || !all or raise Error, "Can't use --all when database is given"
|
119
|
+
return if !Rdbms.exist_database?(database)
|
116
120
|
if database
|
117
121
|
check_owned(database)
|
118
122
|
dbs = [database]
|
@@ -123,10 +127,24 @@ module Prick
|
|
123
127
|
dbs += Rdbms.list_databases(Prick.tmp_databases_re(project.name)) # FIXME: Only used in dev
|
124
128
|
dbs.each { |db|
|
125
129
|
Rdbms.drop_database(db)
|
130
|
+
drop_users db
|
126
131
|
mesg "Dropped database #{db}"
|
127
132
|
}
|
128
133
|
end
|
129
134
|
|
135
|
+
# Drop all users that follows the per-database-user naming convention
|
136
|
+
# ("<database>__<username>")
|
137
|
+
def drop_users(database)
|
138
|
+
PgConn.new("postgres") { |postgres|
|
139
|
+
users = postgres.role.list.grep(/^#{database.sub(/-/, "_")}__/)
|
140
|
+
if postgres.rdbms.exist?(database)
|
141
|
+
PgConn.new(database) { |conn| conn.role.drop users, cascade: true }
|
142
|
+
else
|
143
|
+
postgres.role.drop users
|
144
|
+
end
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
130
148
|
# `select` is a tri-state variable that can be :tables, :no_tables, or :all
|
131
149
|
# (or any other value)
|
132
150
|
def diff(from, to, mark, select)
|
@@ -267,240 +285,3 @@ module Prick
|
|
267
285
|
end
|
268
286
|
end
|
269
287
|
|
270
|
-
__END__
|
271
|
-
|
272
|
-
|
273
|
-
module Prick
|
274
|
-
class Program
|
275
|
-
def project() @project ||= Project.load end
|
276
|
-
|
277
|
-
attr_accessor :quiet
|
278
|
-
attr_accessor :verbose
|
279
|
-
|
280
|
-
def initialize(quiet: false, verbose: false)
|
281
|
-
@quiet = quiet
|
282
|
-
@verbose = verbose
|
283
|
-
end
|
284
|
-
|
285
|
-
def mesg(*args) puts args.compact.grep(/\S/).join(' ') if !quiet end
|
286
|
-
def verb(*args) puts args.compact.grep(/\S/).join(' ') if verbose end
|
287
|
-
def check_clean() Git.clean? or raise Error, "Repository is dirty - please commit your changes first" end
|
288
|
-
|
289
|
-
def initialize_directory(project_name, database_user, directory)
|
290
|
-
!Project.initialized?(directory) or raise Error, "Directory #{directory} is already initialized"
|
291
|
-
Project.initialize_directory(project_name, database_user, directory)
|
292
|
-
if project_name != File.basename(directory)
|
293
|
-
mesg "Initialized project #{project_name} in #{directory}"
|
294
|
-
else
|
295
|
-
mesg "Initialized project #{project_name}"
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
def info
|
300
|
-
if project.tag?
|
301
|
-
puts "At v#{project.version} tag"
|
302
|
-
else
|
303
|
-
puts "On branch #{project.branch.name}"
|
304
|
-
end
|
305
|
-
puts " Git is " + (Git.clean? ? "clean" : "dirty")
|
306
|
-
bv = project.branch.version
|
307
|
-
dv = project.database.version
|
308
|
-
sv = project.branch.schema.version
|
309
|
-
puts " Database version: #{dv}" + (dv != bv ? " (mismatch)" : "")
|
310
|
-
puts " Schema version : #{sv}" + (sv != bv ? " (mismatch)" : "")
|
311
|
-
end
|
312
|
-
|
313
|
-
# TODO: Move to project to take advantage of cache
|
314
|
-
def build(database, version, no_cache)
|
315
|
-
version = version && Version.new(version)
|
316
|
-
into_mesg = database && "into #{database}"
|
317
|
-
database = database ? Database.new(database, project.user) : project.database(version)
|
318
|
-
if version
|
319
|
-
Git.tag?(version) or raise Error, "Can't find tag v#{version}"
|
320
|
-
cache_file = project.cache_file(version)
|
321
|
-
if !no_cache && File.exist?(cache_file)
|
322
|
-
project.load(cache_file, database: database)
|
323
|
-
mesg "Loaded v#{version}", into_mesg, "from cache"
|
324
|
-
else
|
325
|
-
project.build(database: database, version: version)
|
326
|
-
project.save(cache_file, database: database)
|
327
|
-
mesg "Built v#{version}", into_mesg
|
328
|
-
end
|
329
|
-
else
|
330
|
-
project.build(database: database)
|
331
|
-
mesg "Built current schema", into_mesg
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
def load(database, file_or_version)
|
336
|
-
version = Version.try(file_or_version)
|
337
|
-
into_mesg = database && "into #{database}"
|
338
|
-
database = database ? Database.new(database, project.user) : project.database(version)
|
339
|
-
if version
|
340
|
-
file = project.cache_file(version)
|
341
|
-
File.exist?(file) or raise Error, "Can't find #{file} - forgot to build?"
|
342
|
-
project.load(file, database: database)
|
343
|
-
mesg "Loaded v#{version}", into_mesg
|
344
|
-
else
|
345
|
-
file = file_or_version
|
346
|
-
project.load(file, database: database)
|
347
|
-
mesg "Loaded #{file}", into_mesg
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
def save(database, file)
|
352
|
-
file ||= "#{ENV['USER']}-#{name}-#{branch}.sql.gz"
|
353
|
-
subject_mesg = database ? "database #{database}" : "current database"
|
354
|
-
database = database ? Database.new(database, project.user) : project.database(version)
|
355
|
-
project.save(file, database: database)
|
356
|
-
mesg "Saved", subject_mesg, "to #{file}"
|
357
|
-
end
|
358
|
-
|
359
|
-
def make(subject)
|
360
|
-
project.database.exist? or raise Error, "Project database is not present"
|
361
|
-
project.make(project.database, subject)
|
362
|
-
end
|
363
|
-
|
364
|
-
def list_releases(migrations: false, cancelled: false)
|
365
|
-
puts (project.list_releases(all: cancelled) + (migrations ? project.list_migrations : [])).sort.map(&:name)
|
366
|
-
end
|
367
|
-
|
368
|
-
def list_migrations
|
369
|
-
puts project.list_migrations.sort.map(&:name)
|
370
|
-
end
|
371
|
-
|
372
|
-
def list_upgrades(from = nil, to = nil)
|
373
|
-
from = from ? Version.new(from) : project.database.version
|
374
|
-
to = to ? Version.new(to) : project.branch.version
|
375
|
-
branches = project.list_upgrades(from, to)
|
376
|
-
puts branches.map(&:name)
|
377
|
-
end
|
378
|
-
|
379
|
-
def prepare_schema(name)
|
380
|
-
project.prepare_schema(name)
|
381
|
-
mesg project.message
|
382
|
-
end
|
383
|
-
|
384
|
-
def prepare_diff(version = nil)
|
385
|
-
version ||=
|
386
|
-
if project.prerelease? || project.migration? || project.feature?
|
387
|
-
project.branch.base_version
|
388
|
-
else
|
389
|
-
project.branch.version
|
390
|
-
end
|
391
|
-
project.prepare_diff(version)
|
392
|
-
mesg "Remember to update the associated SQL migration files"
|
393
|
-
end
|
394
|
-
|
395
|
-
def prepare_release
|
396
|
-
check_clean
|
397
|
-
project.version.release? or raise Error, "You need to be on a release branch to prepare a release"
|
398
|
-
project.prepare_release
|
399
|
-
mesg project.message
|
400
|
-
end
|
401
|
-
|
402
|
-
def check
|
403
|
-
version ||=
|
404
|
-
if project.prerelease? || project.migration?
|
405
|
-
project.branch.base_version
|
406
|
-
else
|
407
|
-
project.branch.version
|
408
|
-
end
|
409
|
-
project.check_migration(version)
|
410
|
-
end
|
411
|
-
|
412
|
-
# `arg` can be a version numer of a relative increase (eg. 'minor')
|
413
|
-
def create_release(arg = nil)
|
414
|
-
check_clean
|
415
|
-
if project.release?
|
416
|
-
arg or raise Error, "Need a version argument"
|
417
|
-
version = compute_version(project.version, arg)
|
418
|
-
project.create_release(Version.new(version))
|
419
|
-
mesg project.message
|
420
|
-
elsif project.prerelease?
|
421
|
-
arg.nil? or raise Error, "Illegal number of arguments"
|
422
|
-
project.create_release_from_prerelease
|
423
|
-
mesg project.message
|
424
|
-
else
|
425
|
-
raise Error, "You need to be on a release or pre-release branch to create a new release"
|
426
|
-
end
|
427
|
-
end
|
428
|
-
|
429
|
-
def cancel_release(arg)
|
430
|
-
project.cancel_release(Version.new(arg))
|
431
|
-
end
|
432
|
-
|
433
|
-
def create_prerelease(arg)
|
434
|
-
check_clean
|
435
|
-
if project.release?
|
436
|
-
version = %w(major minor patch).include?(arg) ? project.version.increment(arg.to_sym) : Version.new(arg)
|
437
|
-
project.prepare_release(commit: false)
|
438
|
-
prerelease = project.create_prerelease(version)
|
439
|
-
mesg "Created pre-release #{prerelease.version}"
|
440
|
-
elsif project.prerelease?
|
441
|
-
arg.nil? or raise Error, "Illegal number of arguments"
|
442
|
-
prerelease = project.increment_prerelease
|
443
|
-
mesg "Created pre-release #{prerelease.prerelease_version}"
|
444
|
-
else
|
445
|
-
raise Error, "You need to be on a release branch to create a pre-release"
|
446
|
-
end
|
447
|
-
end
|
448
|
-
|
449
|
-
def prepare_migration(arg)
|
450
|
-
check_clean
|
451
|
-
version = Version.new(arg)
|
452
|
-
project.release? or raise "You need to be on a release or migration branch to prepare a migration"
|
453
|
-
project.prepare_migration(version)
|
454
|
-
mesg project.message
|
455
|
-
end
|
456
|
-
|
457
|
-
def create_feature(name)
|
458
|
-
check_clean
|
459
|
-
project.release? or raise "You ned to be on a release branch to create a feature"
|
460
|
-
project.create_feature(name)
|
461
|
-
mesg "Created feature '#{name}'"
|
462
|
-
end
|
463
|
-
|
464
|
-
def include_feature(name_or_version)
|
465
|
-
check_clean
|
466
|
-
project.prerelease? or raise Error, "You need to be on a pre-release branch to include a feature"
|
467
|
-
version = Version.try(name_or_version) ||
|
468
|
-
Version.new(project.branch.base_version, feature: name_or_version)
|
469
|
-
Git.branch?(version.to_s) or raise Error, "Can't find feature #{version}"
|
470
|
-
project.include_feature(version)
|
471
|
-
mesg "Included feature '#{name_or_version}'"
|
472
|
-
mesg "Please resolve eventual conflicts and then commit"
|
473
|
-
end
|
474
|
-
|
475
|
-
def upgrade
|
476
|
-
# TODO: Shutdown connections
|
477
|
-
project.database.version != project.version or raise Error, "Database already up to date"
|
478
|
-
project.backup
|
479
|
-
begin
|
480
|
-
project.upgrade
|
481
|
-
rescue RuntimeError
|
482
|
-
project.restore
|
483
|
-
raise Fail, "Failed upgrading database, rolled back to last version"
|
484
|
-
end
|
485
|
-
end
|
486
|
-
|
487
|
-
def backup(file = nil) project.backup(file) end
|
488
|
-
|
489
|
-
def restore(file = nil)
|
490
|
-
file.nil? || File.exist?(file) or raise Error, "Can't find #{file}"
|
491
|
-
project.restore(file)
|
492
|
-
end
|
493
|
-
|
494
|
-
private
|
495
|
-
def compute_version(version, arg)
|
496
|
-
if arg.nil?
|
497
|
-
nil
|
498
|
-
elsif %w(major minor patch).include?(arg)
|
499
|
-
version.increment(arg.to_sym)
|
500
|
-
else
|
501
|
-
Prick::Version.new(arg)
|
502
|
-
end
|
503
|
-
end
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
data/lib/prick/project.rb
CHANGED
@@ -92,7 +92,7 @@ module Prick
|
|
92
92
|
begin
|
93
93
|
branch = Head.load(name)
|
94
94
|
rescue Version::FormatError
|
95
|
-
|
95
|
+
branch = Prick::UserBranch.new(name)
|
96
96
|
end
|
97
97
|
state = ProjectState.new.read
|
98
98
|
Project.new(state.name, state.user, branch)
|
@@ -222,12 +222,12 @@ module Prick
|
|
222
222
|
end
|
223
223
|
|
224
224
|
def generate_schema
|
225
|
-
build = SchemaBuilder.new(database, SCHEMA_DIR).build(execute: false)
|
225
|
+
build = SchemaBuilder.new(database, state_file, SCHEMA_DIR).build(execute: false)
|
226
226
|
puts build.lines
|
227
227
|
end
|
228
228
|
|
229
229
|
def generate_migration
|
230
|
-
build = MigrationBuilder.new(database, MIGRATION_DIR).build(execute: false)
|
230
|
+
build = MigrationBuilder.new(database, state_file, MIGRATION_DIR).build(execute: false)
|
231
231
|
puts build.lines
|
232
232
|
end
|
233
233
|
|
data/lib/prick/version.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
# Required by gem
|
6
6
|
module Prick
|
7
|
-
VERSION = "0.
|
7
|
+
VERSION = "0.19.0"
|
8
8
|
end
|
9
9
|
|
10
10
|
# Project related code starts here
|
@@ -95,7 +95,7 @@ module Prick
|
|
95
95
|
# Parse a branch or tag name into a Version object. Return a [version, tag]
|
96
96
|
# tuple where tag is true if name was a tag
|
97
97
|
def self.parse(name)
|
98
|
-
name =~ VERSION_RE or raise Version::FormatError, "Expected a version, got #{
|
98
|
+
name =~ VERSION_RE or raise Version::FormatError, "Expected a version, got #{name.inspect}"
|
99
99
|
fork, tag, semver, feature = $1, $2, $3, $4
|
100
100
|
version = Version.new(semver, fork: fork, feature: feature)
|
101
101
|
[version, tag]
|
data/lib/prick.rb
CHANGED
data/prick.gemspec
CHANGED
@@ -34,4 +34,20 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_dependency "shellopts", "2.0.0.pre.14"
|
35
35
|
spec.add_dependency "semantic"
|
36
36
|
spec.add_dependency "indented_io"
|
37
|
+
|
38
|
+
# To be able to call fox in development mode (TODO: Remove)
|
39
|
+
spec.add_dependency "boolean"
|
40
|
+
spec.add_dependency "constrain"
|
41
|
+
spec.add_dependency "developer_exceptions"
|
42
|
+
spec.add_dependency "pg"
|
43
|
+
spec.add_dependency "dry-inflector"
|
44
|
+
|
45
|
+
# In development mode override load paths for gems whose source are located
|
46
|
+
# as siblings of this project directory
|
47
|
+
if File.directory?("#{__dir__}/.git")
|
48
|
+
local_projects = Dir["../*"].select { |path|
|
49
|
+
File.directory?(path) && File.exist?("#{path}/Gemfile")
|
50
|
+
}.map { |relpath| "#{File.absolute_path(relpath)}/lib" }
|
51
|
+
$LOAD_PATH.unshift *local_projects
|
52
|
+
end
|
37
53
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claus Rasmussen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: shellopts
|
@@ -52,6 +52,76 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: boolean
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: constrain
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: developer_exceptions
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pg
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: dry-inflector
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
55
125
|
description: A release control and management system for postgresql
|
56
126
|
email:
|
57
127
|
- claus.l.rasmussen@gmail.com
|