prick 0.29.2 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/TODO +2 -0
  3. data/exe/prick +294 -519
  4. data/idea.txt +38 -0
  5. data/lib/prick/builder/batch.rb +1 -4
  6. data/lib/prick/builder/builder.rb +15 -9
  7. data/lib/prick/builder/node.rb +3 -2
  8. data/lib/prick/builder/node_pool.rb +3 -1
  9. data/lib/prick/builder/parser.rb +2 -1
  10. data/lib/prick/constants.rb +12 -12
  11. data/lib/prick/diff.rb +2 -2
  12. data/lib/prick/environment.rb +30 -12
  13. data/lib/prick/local/command.rb +8 -2
  14. data/lib/prick/local/fmt.rb +56 -0
  15. data/lib/prick/prick_version.rb +1 -1
  16. data/lib/prick/share/init/{.gitignore → dot.gitignore} +1 -1
  17. data/lib/prick/share/init/prick.environment.yml +16 -0
  18. data/lib/prick/share/init/prick.yml +3 -3
  19. data/lib/prick/share/init/schema/prick/build.yml +11 -2
  20. data/lib/prick/share/init/schema/prick/tables.sql +30 -9
  21. data/lib/prick/share/init/schema/prick/views.sql +6 -0
  22. data/lib/prick/state.rb +304 -95
  23. data/lib/prick/subcommand/prick-build.rb +26 -20
  24. data/lib/prick/subcommand/prick-clean.rb +5 -5
  25. data/lib/prick/subcommand/prick-create.rb +42 -7
  26. data/lib/prick/subcommand/prick-drop.rb +41 -14
  27. data/lib/prick/subcommand/prick-fox.rb +1 -1
  28. data/lib/prick/subcommand/prick-init.rb +25 -18
  29. data/lib/prick/subcommand/prick-list.rb +99 -0
  30. data/lib/prick/subcommand/prick-make.rb +8 -8
  31. data/lib/prick/subcommand/prick-migrate.rb +8 -7
  32. data/lib/prick/subcommand/prick-release.rb +4 -2
  33. data/lib/prick/subcommand/prick-set.rb +52 -0
  34. data/lib/prick/subcommand/prick-setup.rb +3 -13
  35. data/lib/prick/subcommand/prick-teardown.rb +13 -9
  36. data/lib/prick/subcommand/subcommand.rb +12 -0
  37. data/lib/prick/version.rb +1 -1
  38. data/lib/prick.rb +54 -15
  39. metadata +14 -9
  40. data/lib/prick/share/init/schema/prick/data.sql +0 -6
  41. data/prick.environments.yml +0 -14
data/idea.txt ADDED
@@ -0,0 +1,38 @@
1
+
2
+ Build spec
3
+ Custom variables
4
+ variables VAR...
5
+
6
+ Variables
7
+ graph # old 'standard'
8
+ schema
9
+ rebuild
10
+ refresh
11
+ require
12
+
13
+ Code
14
+ build
15
+ rebuild
16
+ refresh
17
+ import
18
+ export
19
+
20
+ Special code functions
21
+ super
22
+ build file-or-directory # makes sure not to load a file twice
23
+ sql "sql statement"
24
+ use sql-file # makes sure not to load a file twice
25
+ load dump-files # makes sure not to load a file twice
26
+
27
+ build_ENVIRONMENT # eg. 'build_app_registry_backend'
28
+ rebuild_ENVIRONMENT
29
+ refresh_ENVIRONMENT
30
+
31
+ Environment variables
32
+ PRICK_ENVIRONMENTS # all environments included in the current environment
33
+
34
+ Helper functions
35
+ # Consults PRICK_ENVIRONMENTS to check if the given environment is include
36
+ # in the current environment
37
+ has_environment ENVIRONMENT
38
+
@@ -1,7 +1,7 @@
1
1
  module Prick
2
2
  module Build
3
3
  class BuildBatch
4
- include ::Timer
4
+ include Timer
5
5
 
6
6
  attr_reader :builder
7
7
  forward_to :builder, :conn
@@ -144,9 +144,6 @@ module Prick
144
144
  delete = builder.clean ? :none : :touched
145
145
  begin
146
146
  conn.execute fox.to_sql(format: :exec, delete: delete)
147
-
148
- # FIXME: Why only in FoxBatch - should be set higher up in the system
149
- conn.execute "update prick.versions set built_at = now() at time zone 'UTC'"
150
147
  rescue PG::SyntaxError, PG::Error => ex
151
148
  raise PostgresError.new(ex.message)
152
149
  end
@@ -1,4 +1,4 @@
1
- require_relative '../local/command'
1
+ require_relative '../local/command.rb'
2
2
 
3
3
  require_relative './node.rb'
4
4
  require_relative './node_pool.rb'
@@ -103,18 +103,24 @@ module Prick
103
103
  end
104
104
 
105
105
  def execute(conn, create_schemas: schemas)
106
+ # Group batches
106
107
  group if batches.nil?
108
+
109
+ # Register build in database
110
+ Prick.state.save_build_begin
111
+
112
+ # Create schemas
107
113
  conn.exec create_schemas.map { |schema| "create schema #{schema}" }
108
- for batch in batches
109
- batch.execute
110
- end
111
- end
112
114
 
113
- # def setup
114
- # end
115
+ # Execute batch groups
116
+ t0 = Time.now
117
+ batches.each(&:execute)
118
+ t1 = Time.now
115
119
 
116
- # def teardown
117
- # end
120
+ # Register build result in database
121
+ dt = t1 - t0
122
+ Prick.state.save_build_end(true, dt)
123
+ end
118
124
 
119
125
  def dump
120
126
  batches ? batches.each(&:dump) : pool.dump
@@ -140,7 +140,7 @@ module Prick
140
140
  Command.command(Prick.state.bash_environment, command)
141
141
  rescue Command::Error => ex
142
142
  message = "Error executing '#{command}'\n" + ex.stderr.map { |l| " #{l}" }.join("\n")
143
- raise Prick::Error.new(message)
143
+ Prick.error message
144
144
  end
145
145
  end
146
146
  end
@@ -188,12 +188,13 @@ module Prick
188
188
  puts "BuildNode #{path}"
189
189
  indent {
190
190
  puts "schema: #{schema}" if schema
191
+ puts "refresh: #{refresh_schema.to_s}"
191
192
  puts "pg_graph_ignore_schema: #{pg_graph_ignore_schema}"
192
193
  decl_nodes.each(&:dump)
193
194
  for kind in [:init, :term, :seed]
194
195
  kind_nodes = self.send("#{kind}_nodes".to_sym)
195
196
  if !kind_nodes.empty?
196
- puts "#{kind.upcase}:"
197
+ puts "#{kind}:"
197
198
  indent { kind_nodes.each(&:dump) }
198
199
  end
199
200
  end
@@ -25,7 +25,7 @@ module Prick
25
25
  def build_nodes() nodes.select { |node| node.is_a?(BuildNode) } end
26
26
 
27
27
  # List of schemas to ignore when building PgGraph objects. This is used
28
- # to exclude foreign schemes that doesn't follow the PgGraph naming
28
+ # to exclude foreign schemas that doesn't follow the PgGraph naming
29
29
  # conventions
30
30
  def pg_graph_ignore_schemas()
31
31
  all_nodes.select { |node|
@@ -33,6 +33,8 @@ module Prick
33
33
  }.map(&:schema).uniq
34
34
  end
35
35
 
36
+ # List of schemas that should be rebuilt. #refresh_schemas is not cached
37
+ # and can be manipulated by removing build nodes from the pool
36
38
  def refresh_schemas()
37
39
  build_nodes.select(&:refresh_schema).map(&:schema).uniq
38
40
  end
@@ -130,7 +130,8 @@ module Prick
130
130
  rest = $3
131
131
  args = expand_string(rest || '').split
132
132
  path = expand_filename(dir, command)
133
- path || optional or raise Error, "Can't find '#{entry}' in #{dir}/ from #{unit}"
133
+ path || optional or
134
+ raise Error, "Can't find file\n #{command}\n #{path}\n in #{dir}/\n from #{unit}"
134
135
  !path.nil? or return nil
135
136
  else
136
137
  raise Error, "Not a file name: '#{entry}'"
@@ -7,8 +7,8 @@ module Prick
7
7
  ### DIRECTORIES AND FILE NAMES
8
8
 
9
9
  # Shared files (part of the installation)
10
- SHARE_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/share"
11
- LIBEXEC_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/libexec"
10
+ SHARE_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/prick/share"
11
+ LIBEXEC_PATH = "#{File.dirname(File.dirname(__dir__))}/lib/prick/libexec"
12
12
 
13
13
  # Project directories
14
14
  DIRS = [
@@ -30,19 +30,19 @@ module Prick
30
30
  PRICK_DIR = Dir.getwd
31
31
 
32
32
  # Search path for executables
33
- PRICK_PATH = "#{ENV['PATH']}:#{PRICK_DIR}/#{BIN_DIR}:#{PRICK_DIR}/#{LIBEXEC_DIR}"
33
+ # PRICK_PATH = "#{ENV['PATH']}:#{PRICK_DIR}/#{BIN_DIR}:#{PRICK_DIR}/#{LIBEXEC_DIR}"
34
34
 
35
- # Project specification file
35
+ # Project file
36
36
  PRICK_PROJECT_FILE = "prick.yml"
37
37
  PRICK_PROJECT_PATH = PRICK_PROJECT_FILE
38
38
 
39
- # # Environment specification file
40
- # PRICK_ENVIRONMENT_FILE = "prick.environment.yml"
41
- # PRICK_ENVIRONMENTS_PATH = PRICK_ENVIRONMENT_FILE
39
+ # Environment file
40
+ PRICK_ENVIRONMENT_FILE = "prick.environment.yml"
41
+ PRICK_ENVIRONMENT_PATH = PRICK_ENVIRONMENT_FILE
42
42
 
43
- # Context file
44
- PRICK_CONTEXT_FILE = ".prick-context"
45
- PRICK_CONTEXT_PATH = PRICK_CONTEXT_FILE
43
+ # State file
44
+ PRICK_STATE_FILE = ".prick.state.yml"
45
+ PRICK_STATE_PATH = PRICK_STATE_FILE
46
46
 
47
47
  # Fox state file (contains anchors and table sizes)
48
48
  FOX_STATE_FILE = ".fox-state.yml"
@@ -69,8 +69,8 @@ module Prick
69
69
  AFTER_TABLES_DIFF_FILE = "diff.after-tables.sql"
70
70
  ]
71
71
 
72
-
73
-
72
+ # Default environment
73
+ DEFAULT_ENVIRONMENT = "default"
74
74
 
75
75
  # Not in use:
76
76
 
data/lib/prick/diff.rb CHANGED
@@ -34,7 +34,7 @@ module Prick
34
34
  file2 = file3 = file1
35
35
  mark = mark.nil? ? true : mark
36
36
  elsif file2.nil? != file3.nil?
37
- raise Prick::Fail, "Either none or both of `file2` and `file3` should be nil"
37
+ Prick.failure "Either none or both of `file2` and `file3` should be nil"
38
38
  else
39
39
  mark = mark.nil? ? false : mark
40
40
  end
@@ -48,7 +48,7 @@ module Prick
48
48
  command = "migra --unsafe --with-privileges postgres:///#{@db1} postgres:///#{@db2}"
49
49
  @diff = Command.command(command, fail: false)
50
50
  [0,2].include?(Command.status) or
51
- raise Prick::Fail, "migrate command failed with status #{Command.status}: #{command}"
51
+ Prick.failure "migrate command failed with status #{Command.status}: #{command}"
52
52
  end
53
53
 
54
54
  # Initialize table change variables
@@ -1,11 +1,21 @@
1
1
  require 'yaml'
2
2
  require 'dsort'
3
3
 
4
+ # TODO:
5
+ # o Check :parent vs. :inherit (or explain the difference). Also check
6
+ # handling of :comment
7
+
4
8
  module Prick
9
+ # An environment as defined in the prick.environment file. The environment is
10
+ # constant across all instances of State so we ensure that it is loaded only
11
+ # once
5
12
  class Environment
6
13
  # Environments name
7
14
  attr_reader :name
8
15
 
16
+ # Environment comment. If not nil, this environment is considered a user-environment
17
+ attr_accessor :comment
18
+
9
19
  # Names of parent environments
10
20
  def parents = @values[:parents]
11
21
 
@@ -18,7 +28,7 @@ module Prick
18
28
  def variables = @@VARIABLES
19
29
 
20
30
  # List of user defined variables
21
- def user_variables = @@VARIABLES - [:parent]
31
+ def user_variables = @@VARIABLES - [:parents, :comment]
22
32
 
23
33
  # Hash from variable to value
24
34
  attr_accessor :values
@@ -34,14 +44,15 @@ module Prick
34
44
 
35
45
  def initialize(name)
36
46
  @name = name
47
+ @comment = nil
37
48
  @values = variables.map { |key| [key, []] }.to_h
38
49
  @effective_values = variables.map { |key| [key, []] }.to_h
39
50
  @@ENVIRONMENTS[name] = self
40
51
  end
41
52
 
42
- def self.load_environments(hash) # hash can be nil
53
+ def self.load_environments(yaml) # hash maps from environment name to object
43
54
  if hash
44
- parse(hash)
55
+ parse(yaml)
45
56
  analyze
46
57
  end
47
58
  end
@@ -79,23 +90,30 @@ module Prick
79
90
  @@SORTED_ENVIRONMENTS = []
80
91
 
81
92
  def self.yaml_to_array(value)
82
- !value.nil? && Array(value).flatten.compact.map(&:to_s).map(&:split).flatten
93
+ case value
94
+ when /\n/; [value.split("\n")]
95
+ when nil ; []
96
+ when String; value.split
97
+ else [value.to_s]
98
+ end
99
+ # !value.nil? && Array(value).flatten.compact.map(&:to_s).map(&:split).flatten
83
100
  end
84
101
 
85
102
  def self.parse(hash)
86
103
  hash = hash.dup
87
- @@VARIABLES = [:parents] + (yaml_to_array(hash.delete("variables")).map(&:to_sym) || [])
88
-
104
+ @@VARIABLES = [:parents, :comment] + yaml_to_array(hash.delete("variables") || "").map(&:to_sym)
89
105
  for environment, settings in hash
90
106
  env = Environment.new(environment)
107
+ settings ||= {}
91
108
  if settings.is_a? Hash
92
109
  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}'"
110
+ if variable == "comment"
111
+ env.comment = value
112
+ else
113
+ variable = (variable == "inherit" ? :parents : variable.to_sym)
114
+ value = yaml_to_array(value)
115
+ variables.include?(variable) or raise ArgumentError, "Illegal variable: '#{variable}'"
116
+ env.values[variable] = value
99
117
  end
100
118
  end
101
119
  else
@@ -128,8 +128,14 @@ module Command
128
128
  STDERR.reopen(pe[1])
129
129
  pe[1].close
130
130
 
131
- env
132
- exec(env.map { |k,v| [k.to_s, v.to_s] }.to_h, cmd)
131
+ # Clean bundler environment so that prick can be run in development mode.
132
+ # The problem is that the bundler environment is inherited by
133
+ # subprocesses and interferes with loading ruby commands. FIXME This is
134
+ # only relevant when running prick in development mode
135
+ ENV.delete_if { |k,v| %w(RUBYOPT RUBYLIB _).include?(k) || k =~ /^BUNDLER?_/ }
136
+ env = env.map { |k,v| [k.to_s, v.to_s] }.to_h # Convert array values to strings
137
+ env.each { |k,v| ENV[k] = v }
138
+ Kernel.exec(env, cmd)
133
139
  }
134
140
 
135
141
  pw[0].close
@@ -0,0 +1,56 @@
1
+
2
+ module Fmt
3
+ # Print table
4
+ def self.puts_table(headers, table)
5
+ widths = headers.map(&:size)
6
+ signs = []
7
+ types = []
8
+ for i in (0...headers.size)
9
+ widths[i] =
10
+ (
11
+ [widths[i]] +
12
+ table.map { |row|
13
+ case value = row[i]
14
+ when TrueClass, FalseClass
15
+ types[i] = 's'
16
+ signs[i] = '-'
17
+ 5
18
+ when NilClass
19
+ types[i] = 's'
20
+ signs[i] = '-'
21
+ 4
22
+ when String
23
+ types[i] = 's'
24
+ signs[i] = '-'
25
+ value.size
26
+ when Integer
27
+ types[i] = 'i'
28
+ signs[i] = ''
29
+ Math.log10(value) + 1
30
+ else
31
+ raise ArgumentError, value
32
+ end
33
+ }
34
+ ).max
35
+ end
36
+
37
+ widths = widths.map { _1 }
38
+
39
+ for width, value in widths.zip(headers)
40
+ printf "%-#{width}s ", value
41
+ end
42
+ puts
43
+ widths.each.with_index { |width, index|
44
+ char = (headers[index] =~ /^\s*$/ ? " " : "-")
45
+ printf "%#{width}s ", char * width
46
+ }
47
+ puts
48
+ for row in table
49
+ for sign, width, type, value in signs.zip(widths, types, row)
50
+ value = (value.nil? ? "-" : value)
51
+ printf "%#{sign}#{width}#{type} ", value
52
+ end
53
+ puts
54
+ end
55
+ end
56
+ end
@@ -77,7 +77,7 @@ module Prick
77
77
  @semver = version.semver.dup
78
78
  @feature = feature || version.feature
79
79
  else
80
- raise Internal, "Expected a String, PrickVersion, or Semantic::Version, got #{version.class}"
80
+ raise ArgumentError, "Expected a String, PrickVersion, or Semantic::Version, got #{version.class}"
81
81
  end
82
82
  end
83
83
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  # Local database and environment
3
- /.prick-context
3
+ /.prick-state.yml
4
4
 
5
5
  # Fox state file
6
6
  /.fox-state.yml
@@ -0,0 +1,16 @@
1
+
2
+ variables:
3
+
4
+ all:
5
+
6
+ production:
7
+ inherit: all
8
+
9
+ development:
10
+ inherit: all
11
+
12
+ test:
13
+ inherit: all
14
+
15
+ default:
16
+ inherit: all
@@ -1,6 +1,6 @@
1
+ ---
2
+ # Initial project configuration file
1
3
  name:
2
- title:
3
- database:
4
- username:
4
+ title:
5
5
  version:
6
6
  prick:
@@ -1,5 +1,14 @@
1
+ # Controlled by the prick command. Don't change
2
+ #
3
+ # Note the SQL scripts are also called directly from 'prick setup' when the
4
+ # database is created because we don't want to parse the whole build
5
+ # environment just to create the prick schema. After the initial setup, the
6
+ # scripts are only run by 'prick build' with the --force option
7
+ #
8
+ # This is hardcoded an should be fixed somehow
9
+ #
1
10
  ---
2
11
  - schema: prick
12
+ refresh: false # Because the build process takes care of this
3
13
  - tables.sql
4
- - seed:
5
- - data.sql
14
+ - views.sql
@@ -1,16 +1,37 @@
1
1
  -- Controlled by prick(1). Don't touch
2
2
  --
3
+ -- TODO
4
+ -- o Replace view with a function to return the current build
3
5
 
4
6
  set search_path to prick;
5
7
 
6
- create table versions (
8
+ create table builds (
7
9
  id integer generated by default as identity primary key,
8
- fork varchar,
9
- major integer not null,
10
- minor integer not null,
11
- patch integer not null,
12
- pre integer,
13
- feature varchar,
14
- version varchar not null,
15
- built_at timestamp without time zone not null default (now() at time zone 'UTC')
10
+
11
+ -- Project name
12
+ name varchar not null,
13
+
14
+ -- Version
15
+ version varchar not null, -- Project version
16
+ prick varchar not null,-- Prick version
17
+
18
+ -- Git
19
+ branch varchar not null,
20
+ rev varchar not null, -- commit ID
21
+ clean boolean not null default true, -- True if git repository is clean
22
+
23
+ -- Environment
24
+ environment varchar not null default 'default',
25
+
26
+ -- Build timestamp. This is the time of the start of the build
27
+ built_at timestamp without time zone default (now() at time zone 'UTC'),
28
+
29
+ -- Build status
30
+ success boolean default false,-- True if the database built successfully
31
+
32
+ -- Durations
33
+ duration float,-- Duration of database build. Null if success is false
34
+ prick_duration float, -- Duration of the full prick command
35
+ make_duration float -- Duration of enclosing make script
16
36
  );
37
+
@@ -0,0 +1,6 @@
1
+ create view versions as
2
+ select *
3
+ from builds
4
+ where (select count(*) from builds) = 1
5
+ or built_at = (select max(built_at) from builds)
6
+