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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '022876c913aadf3a2b42646f514f51c2db481c32db42a5903f2dbce88303d423'
4
- data.tar.gz: 1772f43a5a99fe93b97e748488f3c5bfa93fddce768176744ebb27e983cbb475
3
+ metadata.gz: ec055b6544046998736dddf822f2ff0563c6e16deeae039f586ec96651ce5b65
4
+ data.tar.gz: 2687ec86827fc0c808a916462b34f7b71ab993fe0d7d5ef5444d4f5775ce91e2
5
5
  SHA512:
6
- metadata.gz: d8c647cc168aee7f0aab47d39d6ce1050331a06b6aa7a04fc8173152618432c377eb7ce42784eb95d2a70134be10bddd0ff3a43298c2e20af8a3649dc8227451
7
- data.tar.gz: 84068d08a68d2b1177054faa4d31218bf7bc66cf437bf255a6ff2404c0126e10288e58519e11fe7c80029c7169ff9e3a59be5327c39f8fd112bc0a846519d833
6
+ metadata.gz: c278a8a151aefeb23c40dc2468fd271e14b45a031f73942453157b3e8789209fa55094bde9d92276fa85c530c204795e8474bd688ccfca139b7945501f373e73
7
+ data.tar.gz: 4d2df4c44b4c10e13d89ee584e5c1839b963d5c7bff42f5618c20b66173f540383cf1bc3059670a5af4922c4ad594a5ceaddc617cdbf2f54a60f2c8a96ce4653
data/TODO CHANGED
@@ -1,3 +1,4 @@
1
+ o eval vs exit
1
2
  o Make it possible to define (nested) environments
2
3
  o Use standard directory layout
3
4
 
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.yml
37
+ Override database name. Default is read from .prick.context
42
38
 
43
39
  -U,username=USERNAME
44
- Override username from from prick.yml
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), or 'all'. It is
83
- not an error if the object doesn't exist. TODO Only 'users' is currently defined
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
- else
194
- raise NotImplementedError
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
- database, username, args.expect(0..1), force: cmd.force?, timer: cmd.time?, dump: dump)
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 :data, :schema, :database
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 subject: #{subject.inspect}"
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
- # A SQL batch allows the first node to be an executable
36
- if nodes.first.is_a?(ExeNode)
37
- time "Execute script" do
38
- sql = [nodes.first.source]
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::SyntaxError, PG::Error => ex
44
- error, line, pos = parse_pg_message(ex.message)
45
- if line
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
- if node.is_a?(SqlNode)
63
- message = ["prick: #{error} in #{node.path}", line, pos].compact.join(":")
64
- else
65
- message = "prick: #{error} from #{node.path}"
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
- raise
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, "prick: #{ex.message}"
151
+ raise PostgresError.new(ex.message)
143
152
  end
144
153
  }
145
154
  t_type.emit
@@ -10,7 +10,18 @@ include Constrain
10
10
  module Prick
11
11
  module Build
12
12
  class Error < StandardError; end
13
- class PostgresError < Error; end
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 ExeNode < Node
125
- # Using a pipe instead of just executing the command shaves off some
126
- # deciseconds spent starting up bash. It expects the process to read
127
- # database/username from standard input
128
- attr_reader :pipe
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 read_source
138
+ def execute_command
139
139
  begin
140
- pipe.puts [conn.name, conn.user]
141
- sql = pipe.wait
140
+ Command.command(Prick.state.bash_environment, command)
142
141
  rescue Command::Error => ex
143
- message = "prick: Error executing '#{to_s}'\n" + ex.stderr.map { |l| " #{l}" }.join("\n")
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
@@ -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
- # Expand $ENVIRONMENT variable in file names. The function implements a
81
- # hierarchy of environments:
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
- filename = $1
128
+ command = $1
138
129
  optional = !$2.nil?
139
- args = expand_string($3 || '').split
140
- file = expand_filename(dir, filename)
141
- file || optional or raise Error, "Can't find #{filename} in #{dir} from #{unit}"
142
- !file.nil? or return nil
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 = "#{dir}/#{file}"
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, name, file, klass, command, args = parse_file_entry(unit, dir, value)) or return nil
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, "#{dir}/#{file}", klass, command, args)
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, name, file, args = parse_file_entry(unit, dir, value)) or return nil
176
- ExeNode.new(unit, phase, path, args)
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, name, file, args = parse_file_entry(unit, dir, entry)) or return nil
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
- if File.executable? path
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 #{name} in #{dir} from #{unit}"
182
+ raise Error, "Can't find '#{entry}' in #{dir}/ from #{unit}"
199
183
  end
200
184
  end
201
185
  end
@@ -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 (Symbol) to variable value
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 = ($1 || $2).to_sym
21
- variables[key] || match
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
@@ -0,0 +1,6 @@
1
+
2
+ class String
3
+ ANSI_BOLD_START = "\e[1m"
4
+ ANSI_BOLD_STOP = "\e[22m"
5
+ def bold = "#{ANSI_BOLD_START}#{self.to_s}#{ANSI_BOLD_STOP}"
6
+ end
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 standard-error but if :stderr is true,
101
- # #command will return a tuple of standard-output, standard-error lines. If
102
- # :stderr is false, standard-error is ignored and is the same as adding
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 return with an
106
- # exit code != 0 unless :fail is false. In that case the the exit code can be
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
- exec(cmd)
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
@@ -14,8 +14,10 @@ module Prick
14
14
  DIRS = [
15
15
  MIGRATION_DIR = "migration",
16
16
  SCHEMA_DIR = "schema",
17
- PRICK_DIR = "#{SCHEMA_DIR}/prick",
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(PRICK_DIR, SCHEMA_VERSION_FILE)
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
- def self.load
40
- begin
41
- h = YAML.load(File.read PRICK_PROJECT_PATH)
42
- rescue Errno::ENOENT
43
- raise Prick::Error, "Can't open project file: #{PRICK_PROJECT_PATH}"
44
- end
45
- state = State.new
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
- begin
52
- h = YAML.load(File.read PRICK_CONTEXT_PATH)
53
- rescue Errno::ENOENT
54
- raise Prick::Error, "Can't open environment file: #{PRICK_CONTEXT_PATH}"
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
- state.environment = h["environment"]&.to_sym
57
- state.database = h["database"]
58
- state.username = h["username"]
88
+ end
59
89
 
60
- # TODO Load schema version
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, :environment, :database, :username]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prick
4
- VERSION = "0.28.0"
4
+ VERSION = "0.29.0"
5
5
  end
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(database, username, schema, builddir: "schema", force: false, timer: nil, dump: nil)
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
- rescue Prick::Error => ex
72
- $stderr.puts ex.message
73
- exit 1
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.drop_users(database)
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
- PgConn.new("postgres") { |conn|
19
- users = conn.role.list(database: database)
20
- PgConn.new(database) { |db| db.role.drop(users, cascade: true) }
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
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "postspec"
39
39
  spec.add_dependency "pg_graph"
40
40
  spec.add_dependency "shellopts"
41
+ spec.add_dependency "dsort"
41
42
 
42
43
  spec.add_development_dependency "ruby-prof"
43
44
 
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.28.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-08 00:00:00.000000000 Z
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.7
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