prick 0.28.0 → 0.29.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 +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
|