prick 0.28.0 → 0.29.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/TODO +1 -0
- data/exe/prick +68 -20
- data/lib/builder/batch.rb +36 -27
- data/lib/builder/builder.rb +12 -1
- data/lib/builder/node.rb +23 -19
- data/lib/builder/parser.rb +69 -85
- data/lib/ext/expand_variables.rb +16 -3
- data/lib/local/ansi.rb +6 -0
- data/lib/local/command.rb +15 -17
- data/lib/prick/constants.rb +14 -2
- data/lib/prick/environment.rb +134 -0
- data/lib/prick/state.rb +94 -23
- data/lib/prick/version.rb +1 -1
- data/lib/prick.rb +3 -0
- data/lib/subcommand/prick-build.rb +9 -7
- data/lib/subcommand/prick-clean.rb +17 -0
- data/lib/subcommand/prick-drop.rb +11 -6
- data/prick.environments.yml +14 -0
- data/prick.gemspec +1 -0
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec055b6544046998736dddf822f2ff0563c6e16deeae039f586ec96651ce5b65
|
4
|
+
data.tar.gz: 2687ec86827fc0c808a916462b34f7b71ab993fe0d7d5ef5444d4f5775ce91e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c278a8a151aefeb23c40dc2468fd271e14b45a031f73942453157b3e8789209fa55094bde9d92276fa85c530c204795e8474bd688ccfca139b7945501f373e73
|
7
|
+
data.tar.gz: 4d2df4c44b4c10e13d89ee584e5c1839b963d5c7bff42f5618c20b66173f540383cf1bc3059670a5af4922c4ad594a5ceaddc617cdbf2f54a60f2c8a96ce4653
|
data/TODO
CHANGED
data/exe/prick
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
#$LOAD_PATH.unshift "/home/clr/prj/shellopts/lib"
|
4
|
-
#$LOAD_PATH.unshift "/home/clr/prj/pg_graph/lib"
|
5
|
-
#$LOAD_PATH.unshift "/home/clr/prj/fixture_fox/lib"
|
6
|
-
|
7
3
|
require 'bootsnap'
|
8
4
|
begin
|
9
5
|
cache_dir = "/run/user/" + `id -u`.chomp
|
@@ -38,10 +34,19 @@ SPEC = %(
|
|
38
34
|
Change to directory DIR before doing anything else
|
39
35
|
|
40
36
|
-d,database=DATABASE
|
41
|
-
Override database name from prick.
|
37
|
+
Override database name. Default is read from .prick.context
|
42
38
|
|
43
39
|
-U,username=USERNAME
|
44
|
-
Override username
|
40
|
+
Override username. Default is read from .prick.context
|
41
|
+
|
42
|
+
-e,environment=ENVIRONMENT
|
43
|
+
Override environment. Default is read from from .prick.context
|
44
|
+
|
45
|
+
-p,project=PRICK-FILE
|
46
|
+
Override default environment file ('prick.yml')
|
47
|
+
|
48
|
+
-c,context=CONTEXT-FILE
|
49
|
+
Override default context file ('.prick.context')
|
45
50
|
|
46
51
|
version!
|
47
52
|
Print project version
|
@@ -62,6 +67,9 @@ SPEC = %(
|
|
62
67
|
teardown!
|
63
68
|
Drop the database and the database user. TODO: Also run teardown scripts
|
64
69
|
|
70
|
+
clean!
|
71
|
+
Empties the database and drops related users except the database user
|
72
|
+
|
65
73
|
create.data!
|
66
74
|
create.schema!
|
67
75
|
create.database!
|
@@ -79,8 +87,9 @@ SPEC = %(
|
|
79
87
|
drop! -- [KIND]
|
80
88
|
@ Drop objects
|
81
89
|
|
82
|
-
Kind can be 'users', 'data', 'schema', 'database' (the default),
|
83
|
-
not an error if the object doesn't exist. TODO
|
90
|
+
Kind can be 'users', 'data', 'schema', 'user', 'database' (the default),
|
91
|
+
or 'all'. It is not an error if the object doesn't exist. TODO 'data' and
|
92
|
+
'schema' is not implemented
|
84
93
|
|
85
94
|
build! -f,force -t,time --dump=KIND? -- [SCHEMA]
|
86
95
|
Build the project. If SCHEMA is defined, later schemas are excluded.
|
@@ -98,7 +107,10 @@ SPEC = %(
|
|
98
107
|
|
99
108
|
fox! -- FILE...
|
100
109
|
Load fox file data. Data are reset to their initial state after build
|
101
|
-
before the fox data are loaded
|
110
|
+
before the fox data are loaded. This makes it possible to experiment with
|
111
|
+
different data sets
|
112
|
+
|
113
|
+
# TODO: A --clean option that resets data
|
102
114
|
|
103
115
|
release! -- KIND
|
104
116
|
Create a release of the given kind. KIND can be 'major', 'minor', or
|
@@ -112,6 +124,7 @@ SPEC = %(
|
|
112
124
|
dump.data!
|
113
125
|
dump.schema!
|
114
126
|
dump.database!
|
127
|
+
dump.environment!
|
115
128
|
TODO
|
116
129
|
|
117
130
|
dump.migration! --force -- VERSION
|
@@ -154,13 +167,18 @@ begin
|
|
154
167
|
# Check state
|
155
168
|
File.exist?(PRICK_PROJECT_FILE) or raise Prick::Error, "Not in a prick project directory"
|
156
169
|
|
170
|
+
# Handle -p and -c
|
171
|
+
project_file = opts.project || PRICK_PROJECT_PATH
|
172
|
+
context_file = opts.context || PRICK_CONTEXT_PATH
|
173
|
+
|
157
174
|
# Load state
|
158
|
-
Prick.state = State.load
|
175
|
+
state = Prick.state = State.load(project_file, context_file)
|
176
|
+
|
177
|
+
# Set database, username and environment
|
178
|
+
database = state.database = opts.database || state.database
|
179
|
+
username = state.username = opts.username || state.username
|
180
|
+
environment = state.environment = opts.environment || state.environment
|
159
181
|
|
160
|
-
# Handle -d and -U options
|
161
|
-
database = opts.database || Prick.state.database
|
162
|
-
username = opts.username || Prick.state.username
|
163
|
-
|
164
182
|
# Expect a sub-command
|
165
183
|
cmd = opts.subcommand! or raise Prick::Error, "Subcomand expected"
|
166
184
|
|
@@ -180,6 +198,9 @@ begin
|
|
180
198
|
when :teardown!
|
181
199
|
Prick::SubCommand.teardown(database, username)
|
182
200
|
|
201
|
+
when :clean!
|
202
|
+
Prick::SubCommand.clean(database)
|
203
|
+
|
183
204
|
when :create!
|
184
205
|
create_command = opts.create!
|
185
206
|
case create_command.subcommand
|
@@ -190,14 +211,15 @@ begin
|
|
190
211
|
username, version,
|
191
212
|
force: create_command.subcommand!.force?,
|
192
213
|
file: create_command.subcommand!.file)
|
193
|
-
|
194
|
-
|
214
|
+
else
|
215
|
+
raise NotImplementedError
|
195
216
|
end
|
196
217
|
|
197
218
|
when :build!
|
198
219
|
dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
|
220
|
+
# exclude = cmd.exclude? ? cmd.exclude.split(",") : []
|
199
221
|
Prick::SubCommand.build(
|
200
|
-
|
222
|
+
database, username, args.expect(0..1), force: cmd.force?, timer: cmd.time?, dump: dump)
|
201
223
|
|
202
224
|
when :make!
|
203
225
|
dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
|
@@ -212,6 +234,8 @@ begin
|
|
212
234
|
Prick::SubCommand.drop_all(database)
|
213
235
|
when :users
|
214
236
|
Prick::SubCommand.drop_users(database)
|
237
|
+
when :user
|
238
|
+
Prick::SubCommand.drop_user(username)
|
215
239
|
when :database
|
216
240
|
Prick::SubCommand.drop_database(database)
|
217
241
|
when :data, :schema
|
@@ -233,20 +257,44 @@ begin
|
|
233
257
|
when :dump!
|
234
258
|
subject = cmd.subcommand!
|
235
259
|
case cmd.subcommand
|
236
|
-
when :migration
|
260
|
+
when :migration!
|
237
261
|
arg = args.expect(1)
|
238
262
|
version = PrickVersion.try(arg) or raise "Illegal version number: #{arg}"
|
239
263
|
Prick::SubCommand.create_migration(username, version, force: subject.force?, file: "/dev/stdout")
|
240
|
-
when :
|
264
|
+
when :type!
|
265
|
+
conn = PgConn.new(database, username)
|
266
|
+
builder = Prick::Build::Builder.new(conn, Prick::SCHEMA_DIR)
|
267
|
+
meta = PgMeta.new(conn, exclude_schemas: builder.pg_graph_ignore_schemas)
|
268
|
+
graph = PgGraph::Type.new(meta, builder.reflections_file)
|
269
|
+
graph.dump
|
270
|
+
when :data!, :schema!, :database!
|
241
271
|
raise NotImplementedError
|
272
|
+
when :environment!
|
273
|
+
puts state.bash_source
|
242
274
|
else
|
243
|
-
raise Prick::Error, "Unknown
|
275
|
+
raise Prick::Error, "Unknown dump object: #{cmd.subcommand!.__name__}"
|
244
276
|
end
|
245
277
|
|
278
|
+
|
246
279
|
else
|
247
280
|
raise Prick::Fail, "Internal error: Unhandled command - #{opts.subcommand.inspect}"
|
248
281
|
end
|
249
282
|
|
283
|
+
rescue Prick::Build::PostgresError => ex
|
284
|
+
$stderr.puts "Error: #{ex.message}"
|
285
|
+
if STDERR.tty?
|
286
|
+
file, lineno, charno = ex.err
|
287
|
+
if file && lineno
|
288
|
+
lines = IO.readlines(file).map(&:chomp)
|
289
|
+
i = lineno - 1
|
290
|
+
from = [i - 10, 0].max
|
291
|
+
to = [i + 10, lines.size].min
|
292
|
+
$stderr.puts
|
293
|
+
$stderr.puts lines[from...i], lines[i].bold, lines[i+1...to]
|
294
|
+
end
|
295
|
+
end
|
296
|
+
exit 1
|
297
|
+
|
250
298
|
rescue RuntimeError, IOError, ShellOpts::Failure, Prick::Fail, Prick::Build::PostgresError => ex
|
251
299
|
ShellOpts.failure(ex.message)
|
252
300
|
|
data/lib/builder/batch.rb
CHANGED
@@ -14,10 +14,10 @@ module Prick
|
|
14
14
|
@nodes = []
|
15
15
|
end
|
16
16
|
|
17
|
-
def execute(&block)
|
17
|
+
def execute(**opts, &block)
|
18
18
|
name = self.class.to_s.sub(/.*::/, "").split(/(?=[A-Z])/).map(&:downcase).join(" ")
|
19
19
|
time "Execute #{name} (nodes: #{nodes.size})" do
|
20
|
-
yield
|
20
|
+
yield(**opts)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -28,23 +28,38 @@ module Prick
|
|
28
28
|
end
|
29
29
|
|
30
30
|
class SqlBatch < BuildBatch
|
31
|
-
def execute
|
31
|
+
def execute(step: true) # This is marginally faster!
|
32
32
|
super {
|
33
33
|
begin
|
34
34
|
sql = []
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
file = nil
|
36
|
+
node = nil
|
37
|
+
|
38
|
+
# A SQL batch allows the first node to be an evaluated or executed node
|
39
|
+
if nodes.first.is_a?(CommandNode)
|
40
|
+
node = nodes.shift
|
41
|
+
sql = [node.source]
|
42
|
+
end
|
43
|
+
|
44
|
+
if step
|
45
|
+
conn.execute sql, silent: true if !sql.empty?
|
46
|
+
for node in nodes
|
47
|
+
conn.execute node.source, silent: true
|
39
48
|
end
|
49
|
+
else
|
50
|
+
node = nil
|
51
|
+
conn.execute sql + nodes[sql.size..-1].map(&:source), silent: true
|
40
52
|
end
|
41
|
-
conn.execute sql + nodes[sql.size..-1].map(&:source)
|
42
53
|
|
43
|
-
rescue PG::
|
44
|
-
error, line,
|
45
|
-
|
54
|
+
rescue PG::Error => ex
|
55
|
+
error, line, char = conn.err
|
56
|
+
file = nil
|
57
|
+
if step
|
58
|
+
;
|
59
|
+
elsif line
|
60
|
+
# Locate error node and make line number relative
|
46
61
|
if line < nodes.first.lines
|
47
|
-
node = nodes.first
|
62
|
+
node = nodes.first
|
48
63
|
else
|
49
64
|
line -= nodes.first.lines
|
50
65
|
node = nodes[1..-1].find { |node|
|
@@ -58,26 +73,20 @@ module Prick
|
|
58
73
|
end
|
59
74
|
}
|
60
75
|
end
|
76
|
+
# file = node.path
|
77
|
+
end
|
61
78
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
79
|
+
if node.is_a?(SqlNode)
|
80
|
+
message = ["#{error} in #{node.path}", line, char].compact.join(":")
|
81
|
+
elsif node
|
82
|
+
message = "#{error} from #{node.path}"
|
67
83
|
else
|
68
|
-
|
84
|
+
message = conn.errmsg + " in SQL batch"
|
69
85
|
end
|
70
|
-
raise PostgresError.new(message)
|
86
|
+
raise PostgresError.new(message, file, line, char)
|
71
87
|
end
|
72
88
|
}
|
73
89
|
end
|
74
|
-
|
75
|
-
private
|
76
|
-
# Returns [error, line, pos] tuple
|
77
|
-
def parse_pg_message(message)
|
78
|
-
message =~ /ERROR:\s*(.*?)\n(LINE (\d+): ).*?\n(\s*\^)/m or return nil
|
79
|
-
[$1, $3.to_i, $4.size - $2.size]
|
80
|
-
end
|
81
90
|
end
|
82
91
|
|
83
92
|
class ModuleBatch < BuildBatch
|
@@ -139,7 +148,7 @@ module Prick
|
|
139
148
|
# FIXME: Why only in FoxBatch - should be set higher up in the system
|
140
149
|
conn.execute "update prick.versions set built_at = now() at time zone 'UTC'"
|
141
150
|
rescue PG::SyntaxError, PG::Error => ex
|
142
|
-
raise PostgresError
|
151
|
+
raise PostgresError.new(ex.message)
|
143
152
|
end
|
144
153
|
}
|
145
154
|
t_type.emit
|
data/lib/builder/builder.rb
CHANGED
@@ -10,7 +10,18 @@ include Constrain
|
|
10
10
|
module Prick
|
11
11
|
module Build
|
12
12
|
class Error < StandardError; end
|
13
|
-
class PostgresError < Error
|
13
|
+
class PostgresError < Error
|
14
|
+
attr_reader :file
|
15
|
+
attr_reader :lineno
|
16
|
+
attr_reader :charno
|
17
|
+
|
18
|
+
def initialize(message, file = nil, lineno = nil, charno = nil)
|
19
|
+
super(message)
|
20
|
+
@file, @lineno, @charno = file, lineno, charno
|
21
|
+
end
|
22
|
+
|
23
|
+
def err = [@file, @lineno, @charno]
|
24
|
+
end
|
14
25
|
|
15
26
|
class Builder
|
16
27
|
# PgConn object
|
data/lib/builder/node.rb
CHANGED
@@ -41,7 +41,7 @@ module Prick
|
|
41
41
|
@source_lines = nil
|
42
42
|
end
|
43
43
|
|
44
|
-
def to_s() [path, args].compact.join(" ") end
|
44
|
+
def to_s() @to_s ||= [path, args].compact.join(" ") end
|
45
45
|
def inspect() to_s end
|
46
46
|
def dump() puts "#{inspect} (#{schema})" end
|
47
47
|
|
@@ -121,41 +121,45 @@ module Prick
|
|
121
121
|
def self.objects() @@objects end
|
122
122
|
end
|
123
123
|
|
124
|
-
class
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
124
|
+
class CommandNode < Node
|
125
|
+
alias_method :command, :to_s
|
126
|
+
|
127
|
+
def filename = File.basename(path)
|
128
|
+
def relpath = path.sub(/^#{Dir.getwd}\//, "")
|
129
129
|
|
130
130
|
def initialize(parent, phase, path, args = nil)
|
131
|
+
constrain args, [String]
|
131
132
|
super(parent, phase, :exe, path, args)
|
132
|
-
@pipe = Command::Pipe.new(to_s, stderr: nil)
|
133
133
|
end
|
134
134
|
|
135
135
|
def inspect() "#{path} #{(args || []).join(" ")}" end
|
136
136
|
|
137
137
|
protected
|
138
|
-
def
|
138
|
+
def execute_command
|
139
139
|
begin
|
140
|
-
|
141
|
-
sql = pipe.wait
|
140
|
+
Command.command(Prick.state.bash_environment, command)
|
142
141
|
rescue Command::Error => ex
|
143
|
-
message = "
|
142
|
+
message = "Error executing '#{command}'\n" + ex.stderr.map { |l| " #{l}" }.join("\n")
|
144
143
|
raise Prick::Error.new(message)
|
145
|
-
|
146
|
-
rescue Errno::EPIPE
|
147
|
-
raise Prick::Error, "Pipe fail in #{path} called from #{parent.path}\n" +
|
148
|
-
"Tip: Read username/database from standard input"
|
149
|
-
end
|
150
|
-
if pipe.status != 0
|
151
|
-
$stderr.puts pipe.error
|
152
|
-
raise Prick::Error, "Failed executing #{path} called from #{parent.path}"
|
153
144
|
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class EvalNode < CommandNode
|
149
|
+
def read_source
|
150
|
+
sql = execute_command
|
154
151
|
@source_lines = 1 + 1 + sql.count("\n")
|
155
152
|
["set search_path to #{schema}, pg_temp;\n", sql]
|
156
153
|
end
|
157
154
|
end
|
158
155
|
|
156
|
+
class ExecNode < CommandNode
|
157
|
+
def read_source
|
158
|
+
execute_command
|
159
|
+
[]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
159
163
|
# A build.yml file node
|
160
164
|
class BuildNode < Node
|
161
165
|
def nodes() @nodes ||= init_nodes + decl_nodes + seed_nodes + term_nodes end
|
data/lib/builder/parser.rb
CHANGED
@@ -34,6 +34,48 @@ module Prick
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
# Search for an executable in path. Return nil if not found
|
38
|
+
def find_executable(filename) # ChatGPT
|
39
|
+
Prick.state.executable_search_path.split(File::PATH_SEPARATOR).each do |directory|
|
40
|
+
path = File.join(directory, filename)
|
41
|
+
return path if File.executable?(path)
|
42
|
+
end
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
# Expand environments variables file name. Iterates through inherited
|
47
|
+
# environments if the file name contain the $ENVIRONMENT variable. Return
|
48
|
+
# nil if not found
|
49
|
+
#
|
50
|
+
# The hierarchy of environments is defined in the PRICK_ENVIRONMENT_FILE
|
51
|
+
#
|
52
|
+
def expand_filename(dir, filename)
|
53
|
+
environment = Prick.state.environment
|
54
|
+
bash_vars = Prick.state.bash_environment
|
55
|
+
|
56
|
+
last = nil
|
57
|
+
for env in [environment] + Environment[environment].ancestors.reverse
|
58
|
+
bash_vars["ENVIRONMENT"] = env
|
59
|
+
file = expand_variables(filename, bash_vars)
|
60
|
+
last ||= (file != last and file) or return nil # return if no ENVIRONMENT substitution
|
61
|
+
path = (file.start_with?("/") ? file : File.join(dir, file))
|
62
|
+
|
63
|
+
# Check for file (may be executable)
|
64
|
+
return path if File.exist?(path)
|
65
|
+
|
66
|
+
# Check for executable in search path
|
67
|
+
path = find_executable(file) and return path if file !~ /\//
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return nil if not found
|
71
|
+
return nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Expand $ENVIRONMENT variable
|
75
|
+
def expand_string(string)
|
76
|
+
expand_variables(string, Prick.state.bash_environment)
|
77
|
+
end
|
78
|
+
|
37
79
|
def parse_directory(parent, dir)
|
38
80
|
build_file = "#{dir}/build.yml".sub(/\/\//, "/")
|
39
81
|
if File.exist? build_file
|
@@ -77,89 +119,25 @@ module Prick
|
|
77
119
|
unit
|
78
120
|
end
|
79
121
|
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
# production
|
84
|
-
# (online)
|
85
|
-
# development
|
86
|
-
# online
|
87
|
-
# offline
|
88
|
-
# backend
|
89
|
-
# frontend
|
90
|
-
# test
|
91
|
-
#
|
92
|
-
# If an environment doesn't exist in #dir, then the higher level
|
93
|
-
# environments are tried in turn. Eg. if the current environment is
|
94
|
-
# 'test', the algorithm expands '$ENVIRONMENT' to 'test', 'offline', and
|
95
|
-
# 'development' until an existing file is found. If no file is found the
|
96
|
-
# the unexpanded value is returned
|
97
|
-
#
|
98
|
-
# NOTE: This is a hardcoded feature for an internal project. It is
|
99
|
-
# configurable in the next major version
|
100
|
-
#
|
101
|
-
def expand_filename(dir, filename)
|
102
|
-
if File.exist?("#{dir}/#{filename}")
|
103
|
-
return filename
|
104
|
-
elsif filename =~ /\$ENVIRONMENT|\$\{ENVIRONMENT\}/
|
105
|
-
env = Prick.state.environment.to_s
|
106
|
-
while true
|
107
|
-
file = expand_variables(filename, ENVIRONMENT: env, PWD: Dir.getwd)
|
108
|
-
return file if File.exist? "#{dir}/#{file}"
|
109
|
-
case env
|
110
|
-
when "production"
|
111
|
-
file = expand_variables(filename, ENVIRONMENT: "online", PWD: Dir.getwd) or return nil
|
112
|
-
return file if File.exist? "#{dir}/#{file}"
|
113
|
-
return nil
|
114
|
-
when "development"; return nil
|
115
|
-
when "online"; env = "development"
|
116
|
-
when "offline"; env = "development"
|
117
|
-
when "backend"; env = "offline"
|
118
|
-
when "frontend"; env = "offline"
|
119
|
-
when "test"; env = "offline"
|
120
|
-
else
|
121
|
-
raise Error, "Illegal env: '#{env}'"
|
122
|
-
end
|
123
|
-
end
|
124
|
-
else
|
125
|
-
return nil
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
# Expand $ENVIRONMENT variable
|
130
|
-
def expand_string(string)
|
131
|
-
expand_variables(string, ENVIRONMENT: Prick.state.environment.to_s, PWD: Dir.getwd)
|
132
|
-
end
|
133
|
-
|
122
|
+
# Returns path, filename, and an array of arguments. It is an error if
|
123
|
+
# the file can't be found unless #optional is true. In that case a nil
|
124
|
+
# value is returned
|
134
125
|
def parse_file_entry(unit, dir, entry)
|
135
126
|
entry = entry.sub(/\/$/, "")
|
136
127
|
if entry =~ /^(\S+?)(\?)?(?:\s+(.+))?\s*$/
|
137
|
-
|
128
|
+
command = $1
|
138
129
|
optional = !$2.nil?
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
130
|
+
rest = $3
|
131
|
+
args = expand_string(rest || '').split
|
132
|
+
path = expand_filename(dir, command)
|
133
|
+
path || optional or raise Error, "Can't find '#{entry}' in #{dir}/ from #{unit}"
|
134
|
+
!path.nil? or return nil
|
143
135
|
else
|
144
136
|
raise Error, "Not a file name: '#{entry}'"
|
145
137
|
end
|
146
|
-
path
|
147
|
-
[path, entry, file, args].flatten
|
138
|
+
[path, Array(args).flatten]
|
148
139
|
end
|
149
140
|
|
150
|
-
# def parse_file_entry(dir, entry)
|
151
|
-
# name = expand_environment(dir, entry)
|
152
|
-
# name.sub!(/\/$/, "")
|
153
|
-
# if name =~ /^(\S+)\s+(.+)$/ # has arguments -> exe
|
154
|
-
# file = $1
|
155
|
-
# args = $2.split
|
156
|
-
# else
|
157
|
-
# file = name
|
158
|
-
# end
|
159
|
-
# path = "#{dir}/#{file}"
|
160
|
-
# [path, name, file, args]
|
161
|
-
# end
|
162
|
-
|
163
141
|
def parse_entry(unit, phase, dir, entry)
|
164
142
|
if entry.is_a?(Hash)
|
165
143
|
entry.size == 1 or raise Error, "sql and module are single-line values"
|
@@ -168,34 +146,40 @@ module Prick
|
|
168
146
|
when "sql"
|
169
147
|
InlineNode.new(unit, phase, unit.path, expand_string(value))
|
170
148
|
when "call"
|
171
|
-
(path,
|
149
|
+
(path, args = parse_file_entry(unit, dir, value)) or return nil
|
150
|
+
args.size >= 2 or raise Error, "Illegal number of arguments"
|
151
|
+
klass = args.shift
|
152
|
+
command = args.shift
|
172
153
|
klass && command or raise "Illegal number of arguments: #{value}"
|
173
|
-
ModuleNode.new(unit, phase,
|
154
|
+
ModuleNode.new(unit, phase, path, klass, command, args)
|
155
|
+
when "eval"
|
156
|
+
(path, args = parse_file_entry(unit, dir, value)) or return nil
|
157
|
+
EvalNode.new(unit, phase, path, args)
|
174
158
|
when "exec"
|
175
|
-
(path,
|
176
|
-
|
159
|
+
(path, args = parse_file_entry(unit, dir, value)) or return nil
|
160
|
+
ExecNode.new(unit, phase, path, args)
|
177
161
|
else
|
178
162
|
raise Error, "Illegal key: #{key}"
|
179
163
|
end
|
180
164
|
else
|
181
|
-
(path,
|
165
|
+
(path, args = parse_file_entry(unit, dir, entry)) or return nil
|
182
166
|
if File.directory? path
|
183
167
|
parse_directory(unit, path)
|
168
|
+
elsif File.executable? path
|
169
|
+
ExecNode.new(unit, phase, path, args)
|
184
170
|
elsif File.file? path
|
185
171
|
case path
|
186
172
|
when /\.sql$/
|
187
173
|
SqlNode.new(unit, phase, path)
|
188
174
|
when /\.fox$/
|
189
175
|
FoxNode.new(unit, :seed, path)
|
176
|
+
when /build-.*\.yml$/
|
177
|
+
parse_build_file(unit, dir, path)
|
190
178
|
else
|
191
|
-
|
192
|
-
ExeNode.new(unit, phase, path, args)
|
193
|
-
else
|
194
|
-
raise Error, "Unrecognized file type #{File.basename(path)} in #{dir}"
|
195
|
-
end
|
179
|
+
raise Error, "Expected executable, fox, or sql file: #{File.basename(path)} in #{dir}"
|
196
180
|
end
|
197
181
|
else
|
198
|
-
raise Error, "Can't find #{
|
182
|
+
raise Error, "Can't find '#{entry}' in #{dir}/ from #{unit}"
|
199
183
|
end
|
200
184
|
end
|
201
185
|
end
|
data/lib/ext/expand_variables.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# backslash. All occurrences of a variable in the string are replaced
|
7
7
|
#
|
8
8
|
# The str argument is the template string and the variables argument is a hash
|
9
|
-
# from variable name (
|
9
|
+
# from variable name (String) to variable value
|
10
10
|
#
|
11
11
|
# Note that the characters '\x00' and '\x01' are used internally and may not be
|
12
12
|
# present in the template string
|
@@ -17,11 +17,24 @@ def expand_variables(str, variables) # ChatGPT
|
|
17
17
|
|
18
18
|
# Expand variables
|
19
19
|
str.gsub!(/\$(\w+)\b|\$\{(\w+)\}/) do |match| # Strange that '\b' is necessary
|
20
|
-
key =
|
21
|
-
|
20
|
+
key = $1 || $2
|
21
|
+
# variables[key] || match
|
22
|
+
variables[key] || ""
|
22
23
|
end
|
23
24
|
|
24
25
|
# Restore escaped characters
|
25
26
|
str.gsub("\x00", '\\').gsub("\x01", '$')
|
26
27
|
end
|
27
28
|
|
29
|
+
# Return true if any of the variables will be expanded
|
30
|
+
def expand_variables?(str, variables)
|
31
|
+
# Replace escaped bashslashes and dollar signs
|
32
|
+
str = str.gsub('\\\\', "\x00").gsub('\\$', "\x01")
|
33
|
+
|
34
|
+
# Look for expansion
|
35
|
+
str.gsub!(/\$(\w+)\b|\$\{(\w+)\}/) do |match| # Strange that '\b' is necessary
|
36
|
+
return true if variables.include?($1 || $2)
|
37
|
+
end
|
38
|
+
|
39
|
+
return false
|
40
|
+
end
|
data/lib/local/ansi.rb
ADDED
data/lib/local/command.rb
CHANGED
@@ -97,16 +97,15 @@ module Command
|
|
97
97
|
# command on standard-input, if it is a IO object that IO object is piped to
|
98
98
|
# the command
|
99
99
|
#
|
100
|
-
# By default #command pass through
|
101
|
-
#
|
102
|
-
#
|
103
|
-
# "2>/dev/null" to the command
|
100
|
+
# By default #command pass through stderr but if :stderr is true, #command
|
101
|
+
# will instead return a tuple of stdout/stderr lines. If :stderr is false,
|
102
|
+
# stderr is ignored and is the same as adding "2>/dev/null" to the command
|
104
103
|
#
|
105
|
-
# #command raises a Command::Error exception if the command
|
106
|
-
#
|
104
|
+
# #command raises a Command::Error exception if the command returns with an exit
|
105
|
+
# code != 0 unless :fail is false. In that case the the exit code can be
|
107
106
|
# fetched from Command::status
|
108
107
|
#
|
109
|
-
def command(cmd, stdin: nil, stderr: nil, fail: true)
|
108
|
+
def command(env = {}, cmd, stdin: nil, stderr: nil, fail: true)
|
110
109
|
cmd = "set -o errexit\nset -o pipefail\n#{cmd}"
|
111
110
|
|
112
111
|
pw = IO::pipe # pipe[0] for read, pipe[1] for write
|
@@ -129,7 +128,8 @@ module Command
|
|
129
128
|
STDERR.reopen(pe[1])
|
130
129
|
pe[1].close
|
131
130
|
|
132
|
-
|
131
|
+
env
|
132
|
+
exec(env.map { |k,v| [k.to_s, v.to_s] }.to_h, cmd)
|
133
133
|
}
|
134
134
|
|
135
135
|
pw[0].close
|
@@ -143,15 +143,14 @@ module Command
|
|
143
143
|
when Array; pw[1].write(stdin.join("\n") + "\n")
|
144
144
|
end
|
145
145
|
pw[1].flush
|
146
|
-
pw[1].close
|
147
146
|
end
|
147
|
+
pw[1].close # Closing standard input so the command doesn't hang on read
|
148
148
|
|
149
149
|
@status = Process.waitpid2(pid)[1].exitstatus
|
150
150
|
|
151
151
|
out = pr[0].readlines.map(&:chomp)
|
152
152
|
err = pe[0].readlines.map(&:chomp)
|
153
153
|
|
154
|
-
pw[1].close if !stdin
|
155
154
|
pr[0].close
|
156
155
|
pe[0].close
|
157
156
|
|
@@ -171,13 +170,6 @@ module Command
|
|
171
170
|
end
|
172
171
|
end
|
173
172
|
|
174
|
-
# Exit status of the last command. FIXME: This is not usable because the
|
175
|
-
# methods raise an exception if the command exited with anything but 0
|
176
|
-
def status() @status end
|
177
|
-
|
178
|
-
# Stored exception when #command is called with :fail true
|
179
|
-
def exception() @exception end
|
180
|
-
|
181
173
|
# Like command but returns true if the command exited with the expected
|
182
174
|
# status. Note that it suppresses standard-error by default
|
183
175
|
#
|
@@ -186,6 +178,12 @@ module Command
|
|
186
178
|
@status == expect
|
187
179
|
end
|
188
180
|
|
181
|
+
# Exit status of the last command
|
182
|
+
def status() @status end
|
183
|
+
|
184
|
+
# Exception of the last command if it failed, otherwise nil TODO What is this used for?
|
185
|
+
def exception() @exception end
|
186
|
+
|
189
187
|
module_function :command
|
190
188
|
module_function :status
|
191
189
|
module_function :exception
|
data/lib/prick/constants.rb
CHANGED
@@ -14,8 +14,10 @@ module Prick
|
|
14
14
|
DIRS = [
|
15
15
|
MIGRATION_DIR = "migration",
|
16
16
|
SCHEMA_DIR = "schema",
|
17
|
-
|
17
|
+
SCHEMA_PRICK_DIR = "#{SCHEMA_DIR}/prick",
|
18
18
|
PUBLIC_DIR = "#{SCHEMA_DIR}/public",
|
19
|
+
BIN_DIR = "bin",
|
20
|
+
LIBEXEC_DIR = "libexec",
|
19
21
|
VAR_DIR = "var",
|
20
22
|
CACHE_DIR = "#{VAR_DIR}/cache",
|
21
23
|
SPOOL_DIR = "#{VAR_DIR}/spool",
|
@@ -24,10 +26,20 @@ module Prick
|
|
24
26
|
SPEC_DIR = "spec"
|
25
27
|
]
|
26
28
|
|
29
|
+
# Prick project root directory
|
30
|
+
PRICK_DIR = Dir.getwd
|
31
|
+
|
32
|
+
# Search path for executables
|
33
|
+
PRICK_PATH = "#{ENV['PATH']}:#{PRICK_DIR}/#{BIN_DIR}:#{PRICK_DIR}/#{LIBEXEC_DIR}"
|
34
|
+
|
27
35
|
# Project specification file
|
28
36
|
PRICK_PROJECT_FILE = "prick.yml"
|
29
37
|
PRICK_PROJECT_PATH = PRICK_PROJECT_FILE
|
30
38
|
|
39
|
+
# # Environment specification file
|
40
|
+
# PRICK_ENVIRONMENT_FILE = "prick.environment.yml"
|
41
|
+
# PRICK_ENVIRONMENTS_PATH = PRICK_ENVIRONMENT_FILE
|
42
|
+
|
31
43
|
# Context file
|
32
44
|
PRICK_CONTEXT_FILE = ".prick-context"
|
33
45
|
PRICK_CONTEXT_PATH = PRICK_CONTEXT_FILE
|
@@ -42,7 +54,7 @@ module Prick
|
|
42
54
|
|
43
55
|
# Schema data file
|
44
56
|
SCHEMA_VERSION_FILE = "data.sql"
|
45
|
-
SCHEMA_VERSION_PATH = File.join(
|
57
|
+
SCHEMA_VERSION_PATH = File.join(SCHEMA_PRICK_DIR, SCHEMA_VERSION_FILE)
|
46
58
|
|
47
59
|
# Rspec temporary directory
|
48
60
|
SPEC_TMP_DIR = "spec"
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'dsort'
|
3
|
+
|
4
|
+
module Prick
|
5
|
+
class Environment
|
6
|
+
# Environments name
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# Names of parent environments
|
10
|
+
def parents = @values[:parents]
|
11
|
+
|
12
|
+
# Ancestors sorted in dependency order
|
13
|
+
def ancestors
|
14
|
+
@ancestors ||= effective_values[:parents].sort_by { |name| @@SORTED_INDEXES[name] }
|
15
|
+
end
|
16
|
+
|
17
|
+
# List of variables including :parents
|
18
|
+
def variables = @@VARIABLES
|
19
|
+
|
20
|
+
# List of user defined variables
|
21
|
+
def user_variables = @@VARIABLES - [:parent]
|
22
|
+
|
23
|
+
# Hash from variable to value
|
24
|
+
attr_accessor :values
|
25
|
+
|
26
|
+
# Hash from variable name to effective value. The effective value is the
|
27
|
+
# sum of this environment's value and its parents' values
|
28
|
+
attr_accessor :effective_values
|
29
|
+
|
30
|
+
def self.[](name) @@ENVIRONMENTS[name] end
|
31
|
+
def self.environment?(name) @@ENVIRONMENTS.key?(name) end
|
32
|
+
def self.environments() @@SORTED_ENVIRONMENTS.map { |key| @@ENVIRONMENTS[key] } end
|
33
|
+
def self.variables() @@VARIABLES end
|
34
|
+
|
35
|
+
def initialize(name)
|
36
|
+
@name = name
|
37
|
+
@values = variables.map { |key| [key, []] }.to_h
|
38
|
+
@effective_values = variables.map { |key| [key, []] }.to_h
|
39
|
+
@@ENVIRONMENTS[name] = self
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.load_environments(hash) # hash can be nil
|
43
|
+
if hash
|
44
|
+
parse(hash)
|
45
|
+
analyze
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# does not include the name of the environment
|
50
|
+
def bash_env
|
51
|
+
user_variables.map { |variable|
|
52
|
+
["PRICK_ENVIRONMENT_#{variable.upcase}", effective_values[variable]]
|
53
|
+
}.to_h
|
54
|
+
end
|
55
|
+
|
56
|
+
def dump
|
57
|
+
puts name
|
58
|
+
indent {
|
59
|
+
values.each { |var, val|
|
60
|
+
next if val.empty?
|
61
|
+
puts "#{var}:"
|
62
|
+
indent { puts val }
|
63
|
+
}
|
64
|
+
effective_values.each { |var, val|
|
65
|
+
next if val.empty?
|
66
|
+
puts "effective_#{var}:"
|
67
|
+
indent { puts val }
|
68
|
+
}
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.dump
|
73
|
+
environments.each(&:dump)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
@@VARIABLES = []
|
78
|
+
@@ENVIRONMENTS = {}
|
79
|
+
@@SORTED_ENVIRONMENTS = []
|
80
|
+
|
81
|
+
def self.yaml_to_array(value)
|
82
|
+
!value.nil? && Array(value).flatten.compact.map(&:to_s).map(&:split).flatten
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.parse(hash)
|
86
|
+
hash = hash.dup
|
87
|
+
@@VARIABLES = [:parents] + (yaml_to_array(hash.delete("variables")).map(&:to_sym) || [])
|
88
|
+
|
89
|
+
for environment, settings in hash
|
90
|
+
env = Environment.new(environment)
|
91
|
+
if settings.is_a? Hash
|
92
|
+
for variable, value in settings
|
93
|
+
variable = (variable == "inherit" ? :parents : variable.to_sym)
|
94
|
+
value = yaml_to_array(value)
|
95
|
+
if variables.include?(variable)
|
96
|
+
env.values[variable] = value
|
97
|
+
else
|
98
|
+
raise ArgumentError, "Illegal variable: '#{variable}'"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
else
|
102
|
+
raise ArgumentError, "Illegal value for '#{environment}': #{settings.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.analyze
|
108
|
+
# Sort environments in dependency order
|
109
|
+
deps = @@ENVIRONMENTS.map { |name, env| [name, env.parents] }
|
110
|
+
@@SORTED_ENVIRONMENTS = DSort.dsort(deps)
|
111
|
+
@@SORTED_INDEXES = @@SORTED_ENVIRONMENTS.map.with_index { |env, idx| [env, idx] }.to_h
|
112
|
+
|
113
|
+
# Check for undeclared inherited environments
|
114
|
+
@@SORTED_ENVIRONMENTS.each { |environment|
|
115
|
+
@@ENVIRONMENTS.key?(environment) or raise ArgumentError, "Can't find '#{inherited}' environment"
|
116
|
+
}
|
117
|
+
|
118
|
+
# Compute effective attribute values by processing environments in
|
119
|
+
# dependency order so that all parents' environments are computed before
|
120
|
+
# the current environment
|
121
|
+
for name in @@SORTED_ENVIRONMENTS
|
122
|
+
env = Environment[name]
|
123
|
+
env.effective_values = env.values.transform_values { |v| v.dup } # Deep-dup
|
124
|
+
for parent in env.parents.dup
|
125
|
+
for var in variables
|
126
|
+
env.effective_values[var].unshift *Environment[parent].effective_values[var]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
env.effective_values.transform_values! { |v| v.uniq }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
data/lib/prick/state.rb
CHANGED
@@ -27,42 +27,79 @@ module Prick
|
|
27
27
|
# Version of prick(1)
|
28
28
|
attr_accessor :prick_version
|
29
29
|
|
30
|
-
# Run-time environment. Can be :production, :development, or :test
|
31
|
-
attr_accessor :environment
|
32
|
-
|
33
30
|
# Name of database
|
34
31
|
attr_accessor :database
|
35
32
|
|
36
33
|
# Name of database user
|
37
34
|
attr_accessor :username
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
state.name = h["name"]
|
47
|
-
state.title = h["title"]
|
48
|
-
state.version = h["version"] && PrickVersion.new(h["version"])
|
49
|
-
state.prick_version = h["prick"] && PrickVersion.new(h["prick"])
|
36
|
+
# Run-time environment name
|
37
|
+
def environment() @environment end
|
38
|
+
def environment=(env)
|
39
|
+
constrain env, String
|
40
|
+
Environment.environment?(env) or raise "Illegal environment: '#{env}'"
|
41
|
+
@environment = env
|
42
|
+
end
|
50
43
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
# Prick project dir. This is not a constant because exe/prick can change
|
45
|
+
# directory (FIXME it can?)
|
46
|
+
def prick_dir
|
47
|
+
@prick_dir ||= Dir.getwd
|
48
|
+
end
|
49
|
+
|
50
|
+
# Prick executable search_path
|
51
|
+
def executable_search_path
|
52
|
+
@executable_search_path ||= "#{ENV['PATH']}:#{prick_dir}/#{BIN_DIR}:#{prick_dir}/#{LIBEXEC_DIR}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Create a bash(1) environment (Hash). It is used for in-prick expansion of
|
56
|
+
# variables and also injected into the enviroment of subprocesses
|
57
|
+
def bash_environment(scope = :global)
|
58
|
+
hash = {
|
59
|
+
"DATABASE" => Prick.state.database, # FIXME: Yt
|
60
|
+
"USERNAME" => Prick.state.username, # FIXME: Yt
|
61
|
+
"ENVIRONMENT" => Prick.state.environment.to_s, # FIXME: Yt
|
62
|
+
"PRICK_NAME" => Prick.state.name,
|
63
|
+
"PRICK_TITLE" => Prick.state.title,
|
64
|
+
"PRICK_VERSION" => Prick.state.version,
|
65
|
+
"PRICK_DATABASE" => Prick.state.database,
|
66
|
+
"PRICK_USERNAME" => Prick.state.username,
|
67
|
+
"PRICK_ENVIRONMENT" => Prick.state.environment.to_s,
|
68
|
+
}
|
69
|
+
case scope
|
70
|
+
when :local; # nop
|
71
|
+
hash.merge Environment[Prick.state.environment].bash_env
|
72
|
+
when :global;
|
73
|
+
ENV.to_h.merge(hash).merge({
|
74
|
+
"PATH" => Prick.state.executable_search_path,
|
75
|
+
"PRICK_SCHEMADIR" => File.join(Prick.state.prick_dir, SCHEMA_DIR),
|
76
|
+
"PRICK_BINDIR" => File.join(Prick.state.prick_dir, BIN_DIR),
|
77
|
+
"PRICK_LIBEXECDIR" => File.join(Prick.state.prick_dir, LIBEXEC_DIR),
|
78
|
+
"PRICK_VARDIR" => File.join(Prick.state.prick_dir, VAR_DIR),
|
79
|
+
"PRICK_CACHEDIR" => File.join(Prick.state.prick_dir, CACHE_DIR),
|
80
|
+
"PRICK_SPOOLDIR" => File.join(Prick.state.prick_dir, SPOOL_DIR),
|
81
|
+
"PRICK_TMPDIR" => File.join(Prick.state.prick_dir, TMP_DIR),
|
82
|
+
"PRICK_CLONEDIR" => File.join(Prick.state.prick_dir, CLONE_DIR),
|
83
|
+
"PRICK_SPECDIR" => File.join(Prick.state.prick_dir, BIN_DIR)
|
84
|
+
}).merge(Environment[Prick.state.environment].bash_env)
|
85
|
+
else
|
86
|
+
raise ArgumentError
|
55
87
|
end
|
56
|
-
|
57
|
-
state.database = h["database"]
|
58
|
-
state.username = h["username"]
|
88
|
+
end
|
59
89
|
|
60
|
-
|
90
|
+
def bash_source
|
91
|
+
bash_environment(:local).map { |var,val| "export #{var}=\"#{Array(val).join(' ')}\"\n" }.join
|
92
|
+
end
|
61
93
|
|
94
|
+
def self.load(project_file, context_file)
|
95
|
+
state = State.new
|
96
|
+
state.send(:parse_project_file, project_file || PRICK_PROJECT_PATH)
|
97
|
+
state.send(:parse_context_file, context_file || PRICK_CONTEXT_PATH)
|
62
98
|
state
|
63
99
|
end
|
64
100
|
|
65
101
|
def save
|
102
|
+
raise NotImplementedError
|
66
103
|
h = {
|
67
104
|
"name" => name,
|
68
105
|
"title" => title,
|
@@ -97,11 +134,45 @@ module Prick
|
|
97
134
|
indent {
|
98
135
|
for method in [
|
99
136
|
:name, :title, :prick_version, :project_version, :schema_version,
|
100
|
-
:database_version, :
|
137
|
+
:database_version, :database, :username]
|
101
138
|
puts "#{method}: #{self.send method}"
|
102
139
|
end
|
140
|
+
puts "environments:"
|
141
|
+
indent { Environment.dump }
|
103
142
|
}
|
104
143
|
end
|
144
|
+
|
145
|
+
private
|
146
|
+
def load_yaml(file, expected_keys, optional_keys)
|
147
|
+
begin
|
148
|
+
hash = YAML.load(File.read file)
|
149
|
+
rescue Errno::ENOENT
|
150
|
+
raise Prick::Error, "Can't read #{file}"
|
151
|
+
end
|
152
|
+
for key in expected_keys
|
153
|
+
!hash[key].to_s.empty? or raise Prick::Error, "Can't find '#{key}' in #{file}"
|
154
|
+
end
|
155
|
+
(unknown = (hash.keys - expected_keys - optional_keys).first) and
|
156
|
+
Prick::Error "Illegal key '#{unknown}' in #{file}"
|
157
|
+
hash
|
158
|
+
end
|
159
|
+
|
160
|
+
def parse_context_file(context_file)
|
161
|
+
hash = load_yaml(context_file, %w(environment database username), [])
|
162
|
+
@database = hash["database"]
|
163
|
+
@username = hash["username"]
|
164
|
+
@environment = hash["environment"]
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse_project_file(prick_file)
|
168
|
+
hash = load_yaml(prick_file, %w(name title), %w(version prick environments))
|
169
|
+
@name = hash["name"]
|
170
|
+
@title = hash["title"]
|
171
|
+
@version = hash["version"] && PrickVersion.new(hash["version"])
|
172
|
+
@prick_version = hash["prick"] && PrickVersion.new(hash["prick"])
|
173
|
+
Environment.load_environments(hash["environments"] || {})
|
174
|
+
end
|
105
175
|
end
|
106
176
|
end
|
107
177
|
|
178
|
+
|
data/lib/prick/version.rb
CHANGED
data/lib/prick.rb
CHANGED
@@ -17,7 +17,9 @@ require 'ext/expand_variables.rb'
|
|
17
17
|
require 'local/command.rb'
|
18
18
|
require 'local/git.rb'
|
19
19
|
require 'local/timer.rb'
|
20
|
+
require 'local/ansi.rb'
|
20
21
|
|
22
|
+
require 'prick/environment.rb'
|
21
23
|
require 'prick/state.rb'
|
22
24
|
require 'prick/prick_version.rb'
|
23
25
|
require 'prick/diff.rb'
|
@@ -33,6 +35,7 @@ end
|
|
33
35
|
require 'subcommand/prick-init.rb'
|
34
36
|
require 'subcommand/prick-setup.rb'
|
35
37
|
require 'subcommand/prick-teardown.rb'
|
38
|
+
require 'subcommand/prick-clean.rb'
|
36
39
|
require 'subcommand/prick-create.rb'
|
37
40
|
require 'subcommand/prick-build.rb'
|
38
41
|
require 'subcommand/prick-make.rb'
|
@@ -3,7 +3,10 @@
|
|
3
3
|
require 'builder/builder.rb'
|
4
4
|
|
5
5
|
module Prick::SubCommand
|
6
|
-
def self.build(
|
6
|
+
def self.build(
|
7
|
+
database, username, schema,
|
8
|
+
builddir: "schema", force: false, timer: nil, dump: nil)
|
9
|
+
|
7
10
|
Timer.on! if timer
|
8
11
|
time "Prick::Command#build" do
|
9
12
|
begin
|
@@ -15,20 +18,19 @@ module Prick::SubCommand
|
|
15
18
|
builder = nil
|
16
19
|
time "Load build object" do
|
17
20
|
if super_conn.rdbms.exist? database
|
18
|
-
conn = PgConn.new(database, username)
|
19
21
|
exist = true
|
20
22
|
else
|
21
23
|
super_conn.rdbms.create database, owner: username
|
22
|
-
conn = PgConn.new(database, username)
|
23
24
|
exist = false
|
24
25
|
end
|
26
|
+
conn = PgConn.new(database, username)
|
25
27
|
|
26
28
|
builder = Prick::Build::Builder.new(conn, builddir)
|
27
29
|
|
28
30
|
if exist
|
29
31
|
if force
|
30
32
|
# Drop all schemas but re-creates the public schema
|
31
|
-
super_conn.rdbms.empty!(database)
|
33
|
+
super_conn.rdbms.empty!(database, public: false)
|
32
34
|
|
33
35
|
else
|
34
36
|
# Find schemas to refresh. This includes all schemas in the
|
@@ -68,9 +70,9 @@ module Prick::SubCommand
|
|
68
70
|
builder.execute conn
|
69
71
|
end
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
-
|
73
|
+
# rescue Prick::Error => ex
|
74
|
+
# $stderr.puts ex.message
|
75
|
+
# exit 1
|
74
76
|
|
75
77
|
rescue ::Command::Error => ex
|
76
78
|
$stderr.puts ex.message
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'builder/builder.rb'
|
4
|
+
|
5
|
+
module Prick::SubCommand
|
6
|
+
# Drop users and empty database. This has the same as effect as
|
7
|
+
# teardown+setup but without recreating the user and the database that
|
8
|
+
# annoyingly terminates all user sessions
|
9
|
+
def self.clean(database)
|
10
|
+
drop_users(database)
|
11
|
+
PgConn.new("postgres") { |conn|
|
12
|
+
conn.rdbms.empty!(database) if conn.rdbms.exist?(database)
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
@@ -3,8 +3,15 @@
|
|
3
3
|
require 'builder/builder.rb'
|
4
4
|
|
5
5
|
module Prick::SubCommand
|
6
|
-
def self.
|
6
|
+
def self.drop_user(username) # No database so does not cascade
|
7
7
|
PgConn.new("postgres") { |conn|
|
8
|
+
conn.role.drop([username])
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Drop all users associated with the given database except the owner
|
13
|
+
def self.drop_users(database)
|
14
|
+
PgConn.new(database) { |conn|
|
8
15
|
users = conn.role.list(database: database)
|
9
16
|
conn.role.drop(users, cascade: true)
|
10
17
|
}
|
@@ -15,11 +22,9 @@ module Prick::SubCommand
|
|
15
22
|
end
|
16
23
|
|
17
24
|
def self.drop_all(database)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
conn.rdbms.drop database
|
22
|
-
}
|
25
|
+
drop_users(database)
|
26
|
+
drop_database(database)
|
27
|
+
drop_user(username)
|
23
28
|
end
|
24
29
|
end
|
25
30
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
online:
|
2
|
+
offline:
|
3
|
+
production: online
|
4
|
+
development: offline
|
5
|
+
frontend: development
|
6
|
+
app_registry_frontend: frontend
|
7
|
+
app_portal_frontend: frontend
|
8
|
+
backend: development
|
9
|
+
app_registry_backend: backend
|
10
|
+
app_portal_backend: backend
|
11
|
+
fdw_import: online
|
12
|
+
sagsys_import: offline
|
13
|
+
app_portal_import: offline
|
14
|
+
test: offline
|
data/prick.gemspec
CHANGED
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.29.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: 2024-01-
|
11
|
+
date: 2024-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: semantic
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: dsort
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: ruby-prof
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -174,12 +188,14 @@ files:
|
|
174
188
|
- lib/builder/node_pool.rb
|
175
189
|
- lib/builder/parser.rb
|
176
190
|
- lib/ext/expand_variables.rb
|
191
|
+
- lib/local/ansi.rb
|
177
192
|
- lib/local/command.rb
|
178
193
|
- lib/local/git.rb
|
179
194
|
- lib/local/timer.rb
|
180
195
|
- lib/prick.rb
|
181
196
|
- lib/prick/constants.rb
|
182
197
|
- lib/prick/diff.rb
|
198
|
+
- lib/prick/environment.rb
|
183
199
|
- lib/prick/prick_version.rb
|
184
200
|
- lib/prick/state.rb
|
185
201
|
- lib/prick/version.rb
|
@@ -203,6 +219,7 @@ files:
|
|
203
219
|
- lib/share/migrate/migration/diff.before-tables.sql
|
204
220
|
- lib/share/migrate/migration/diff.tables.sql
|
205
221
|
- lib/subcommand/prick-build.rb
|
222
|
+
- lib/subcommand/prick-clean.rb
|
206
223
|
- lib/subcommand/prick-create.rb
|
207
224
|
- lib/subcommand/prick-drop.rb
|
208
225
|
- lib/subcommand/prick-fox.rb
|
@@ -212,6 +229,7 @@ files:
|
|
212
229
|
- lib/subcommand/prick-release.rb
|
213
230
|
- lib/subcommand/prick-setup.rb
|
214
231
|
- lib/subcommand/prick-teardown.rb
|
232
|
+
- prick.environments.yml
|
215
233
|
- prick.gemspec
|
216
234
|
homepage: http://www.nowhere.com/
|
217
235
|
licenses: []
|
@@ -231,7 +249,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
231
249
|
- !ruby/object:Gem::Version
|
232
250
|
version: '0'
|
233
251
|
requirements: []
|
234
|
-
rubygems_version: 3.3.
|
252
|
+
rubygems_version: 3.3.18
|
235
253
|
signing_key:
|
236
254
|
specification_version: 4
|
237
255
|
summary: A release control and management system for postgresql
|