prick 0.15.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/TODO +3 -0
- data/exe/prick +29 -11
- data/lib/prick/branch.rb +11 -0
- data/lib/prick/builder.rb +74 -30
- data/lib/prick/command.rb +16 -5
- 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/TODO
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
|
2
|
+
o Add a top-level 'global' directory for stuff that doesn't belong to a schema or that
|
3
|
+
require superuser privileges. prick-build should then execute the content of that
|
4
|
+
directory first before switching to the database user
|
2
5
|
o Check for commits to tags
|
3
6
|
o Accumulate version history in prick.versions instead of just having the newest
|
4
7
|
o Using rc's in migration syntax: fork-version-fork-version.rc1 ?
|
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
|
@@ -198,7 +206,7 @@ begin
|
|
198
206
|
# Change to parent directory containing the Prick version file if not found
|
199
207
|
# in the current directory
|
200
208
|
program.current_directory = Dir.getwd
|
201
|
-
while
|
209
|
+
while Dir.getwd != "/" && !File.exist?(PRICK_VERSION_FILE)
|
202
210
|
Dir.chdir("..")
|
203
211
|
end
|
204
212
|
|
@@ -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,14 +67,23 @@ module Prick
|
|
59
67
|
def self.yml_file(directory) raise NotThis end
|
60
68
|
|
61
69
|
protected
|
62
|
-
def
|
63
|
-
|
64
|
-
|
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
|
65
82
|
end
|
66
83
|
|
67
84
|
def do_sql(path, schema: nil)
|
68
|
-
|
69
|
-
|
85
|
+
puts "do_sql(#{path})" if $verbose >= 2
|
86
|
+
do_path(path) { |file|
|
70
87
|
if @execute
|
71
88
|
begin
|
72
89
|
Rdbms.exec_file(database.name, file, user: database.user, schema: schema)
|
@@ -82,10 +99,29 @@ module Prick
|
|
82
99
|
true
|
83
100
|
end
|
84
101
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
lines = Command.command "
|
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
|
+
|
120
|
+
def do_exe(path, args = [], schema: nil)
|
121
|
+
puts "do_exe(#{path.inspect}, #{args.inspect})" if $verbose >= 2
|
122
|
+
do_path(path) { |file|
|
123
|
+
cmd = (["./#{file}"] + args).join(" ")
|
124
|
+
lines = Command.command cmd, stdin: [database.name, database.user], stderr: nil
|
89
125
|
if @execute
|
90
126
|
begin
|
91
127
|
Rdbms.exec_sql(database.name, lines.join("\n"), user: database.user, schema: schema)
|
@@ -102,20 +138,25 @@ module Prick
|
|
102
138
|
end
|
103
139
|
|
104
140
|
def do_yml(path)
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
+
}
|
108
145
|
true
|
109
146
|
end
|
110
147
|
|
111
|
-
# 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)
|
112
149
|
def build_subject(subject)
|
113
|
-
|
114
|
-
|
150
|
+
puts "build_subject(#{subject.inspect}) in #{Dir.getwd}" if $verbose > 0
|
151
|
+
cmd, *args = subject.split(/\s+/)
|
152
|
+
if File.file?(cmd) && File.executable?(cmd)
|
153
|
+
do_exe(cmd, args)
|
115
154
|
elsif File.file?(subject) && subject.end_with?(".yml")
|
116
155
|
do_yml(subject)
|
117
156
|
elsif File.file?(subject) && subject.end_with?(".sql")
|
118
157
|
do_sql(subject)
|
158
|
+
elsif File.file?(subject) && subject.end_with?(".fox")
|
159
|
+
do_fox(subject)
|
119
160
|
elsif File.exist?(yml_file = "#{subject}.yml")
|
120
161
|
do_yml(yml_file)
|
121
162
|
elsif File.exist?(sql_file = "#{subject}.sql")
|
@@ -128,16 +169,19 @@ module Prick
|
|
128
169
|
end
|
129
170
|
|
130
171
|
def build_directory(path)
|
131
|
-
|
132
|
-
|
133
|
-
|
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?
|
134
176
|
subjects = [default]
|
135
177
|
else
|
178
|
+
candidates = Dir["*"]
|
136
179
|
exes = candidates.select { |file| File.file?(file) && File.executable?(file) }
|
137
180
|
ymls = candidates.select { |file| file.end_with?(".yml") }.map { |f| f.sub(/\.yml$/, "") }
|
138
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$/, "") }
|
139
183
|
dirs = candidates.select { |file| File.directory?(file) }
|
140
|
-
subjects = (exes + ymls + sqls + dirs).uniq.sort
|
184
|
+
subjects = (exes + ymls + sqls + foxs + dirs).uniq.sort #.reject { |f| f != "diff" } FIXME ??
|
141
185
|
end
|
142
186
|
subjects.inject(false) { |a, s| build_subject(s) || a }
|
143
187
|
end
|
@@ -189,13 +233,13 @@ module Prick
|
|
189
233
|
|
190
234
|
protected
|
191
235
|
def do_sql(path)
|
192
|
-
schema = Dir.getwd
|
236
|
+
schema = Dir.getwd =~ /^.*schema\/([^\/]+)(?:\/.*)?$/ ? $1 : "public"
|
193
237
|
super(path, schema: schema)
|
194
238
|
end
|
195
239
|
|
196
|
-
def do_exe(path)
|
197
|
-
schema = Dir.getwd
|
198
|
-
super(path, schema: schema)
|
240
|
+
def do_exe(path, args = [])
|
241
|
+
schema = Dir.getwd =~ /^.*schema\/([^\/]+)(?:\/.*)?$/ ? $1 : "public"
|
242
|
+
super(path, args, schema: schema)
|
199
243
|
end
|
200
244
|
end
|
201
245
|
end
|
data/lib/prick/command.rb
CHANGED
@@ -15,14 +15,17 @@ module Command
|
|
15
15
|
end
|
16
16
|
|
17
17
|
# Execute the shell command 'cmd' and return standard output as an array of
|
18
|
-
# strings
|
18
|
+
# strings. while stderr is passed through unless stderr: is false. If stderr:
|
19
19
|
# is true, it returns a tuple of [stdout, stderr] instead. The shell command
|
20
20
|
# is executed with the `errexit` and `pipefail` bash options
|
21
|
+
#
|
22
|
+
# The :stdin option is a line or an array of lines that'll be fed to the
|
23
|
+
# standard input of the command. Default is nil
|
21
24
|
#
|
22
25
|
# It raises a Command::Error exception if the command fails unless :fail is
|
23
26
|
# true. The exit status of the last command is stored in ::status
|
24
27
|
#
|
25
|
-
def command(cmd, stderr: false, fail: true)
|
28
|
+
def command(cmd, stdin: nil, stderr: false, fail: true)
|
26
29
|
cmd = "set -o errexit\nset -o pipefail\n#{cmd}"
|
27
30
|
|
28
31
|
pw = IO::pipe # pipe[0] for read, pipe[1] for write
|
@@ -52,12 +55,18 @@ module Command
|
|
52
55
|
pr[1].close
|
53
56
|
pe[1].close
|
54
57
|
|
58
|
+
if stdin
|
59
|
+
pw[1].puts(stdin)
|
60
|
+
pw[1].flush
|
61
|
+
pw[1].close
|
62
|
+
end
|
63
|
+
|
55
64
|
@status = Process.waitpid2(pid)[1].exitstatus
|
56
65
|
|
57
66
|
out = pr[0].readlines.collect { |line| line.chop }
|
58
|
-
err = pe[0].readlines.collect { |line| line.chop }
|
67
|
+
err = pe[0].readlines.collect { |line| line.chop }.grep_v(/^NOTICE:/)
|
59
68
|
|
60
|
-
pw[1].close
|
69
|
+
pw[1].close if !stdin
|
61
70
|
pr[0].close
|
62
71
|
pe[0].close
|
63
72
|
|
@@ -65,7 +74,9 @@ module Command
|
|
65
74
|
case stderr
|
66
75
|
when true; [out, err]
|
67
76
|
when false; out
|
68
|
-
when NilClass;
|
77
|
+
when NilClass;
|
78
|
+
$stderr.puts err
|
79
|
+
out
|
69
80
|
else
|
70
81
|
raise Internal, "Unexpected value for :stderr - #{stderr.inspect}"
|
71
82
|
end
|
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
|