textus 0.20.2 → 0.22.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -0
  3. data/README.md +7 -4
  4. data/SPEC.md +42 -3
  5. data/lib/textus/application/reads/audit.rb +40 -15
  6. data/lib/textus/application/reads/pulse.rb +63 -0
  7. data/lib/textus/application/writes/materializer.rb +1 -1
  8. data/lib/textus/application/writes/publish.rb +25 -106
  9. data/lib/textus/{intro.rb → boot.rb} +27 -5
  10. data/lib/textus/builder/pipeline.rb +2 -2
  11. data/lib/textus/cli/verb/audit.rb +2 -0
  12. data/lib/textus/cli/verb/{intro.rb → boot.rb} +3 -3
  13. data/lib/textus/cli/verb/pulse.rb +17 -0
  14. data/lib/textus/cli.rb +1 -1
  15. data/lib/textus/errors.rb +16 -0
  16. data/lib/textus/infra/audit_log.rb +126 -16
  17. data/lib/textus/manifest/entry/base.rb +41 -4
  18. data/lib/textus/manifest/entry/derived.rb +40 -4
  19. data/lib/textus/manifest/entry/intake.rb +15 -3
  20. data/lib/textus/manifest/entry/leaf.rb +6 -5
  21. data/lib/textus/manifest/entry/nested.rb +42 -3
  22. data/lib/textus/manifest/entry/parser.rb +8 -44
  23. data/lib/textus/manifest/entry/validators/events.rb +1 -1
  24. data/lib/textus/manifest/entry/validators/format_matrix.rb +5 -4
  25. data/lib/textus/manifest/entry/validators/index_filename.rb +2 -1
  26. data/lib/textus/manifest/entry/validators/inject_boot.rb +19 -0
  27. data/lib/textus/manifest/entry/validators/publish_each.rb +4 -3
  28. data/lib/textus/manifest/entry/validators.rb +1 -1
  29. data/lib/textus/manifest/entry.rb +3 -0
  30. data/lib/textus/manifest/resolver.rb +4 -4
  31. data/lib/textus/manifest/schema.rb +20 -6
  32. data/lib/textus/manifest.rb +10 -0
  33. data/lib/textus/operations.rb +8 -1
  34. data/lib/textus/store.rb +5 -1
  35. data/lib/textus/version.rb +1 -1
  36. metadata +6 -4
  37. data/lib/textus/manifest/entry/validators/inject_intro.rb +0 -21
@@ -5,6 +5,9 @@ module Textus
5
5
  # constants on Entry. Canonical source is the PublishEach validator.
6
6
  PUBLISH_EACH_VARS = Validators::PublishEach::KNOWN_VARS
7
7
  PUBLISH_EACH_VAR_RE = Validators::PublishEach::VAR_RE
8
+
9
+ # Populated by each Entry::* subclass at load time.
10
+ REGISTRY = {}
8
11
  end
9
12
  end
10
13
  end
@@ -42,7 +42,7 @@ module Textus
42
42
  # entry with nested: true in the raw YAML — e.g. Intake entries covering
43
43
  # a directory of leaf files).
44
44
  def nested_entry?(entry)
45
- entry.is_a?(Textus::Manifest::Entry::Nested) || entry.raw["nested"] == true
45
+ entry.nested?
46
46
  end
47
47
 
48
48
  def build_resolution(entry, remaining, key)
@@ -51,7 +51,7 @@ module Textus
51
51
  else
52
52
  raise UnknownKey.new(key, suggestions: suggestions_for(key)) unless nested_entry?(entry)
53
53
 
54
- index_fn = entry.respond_to?(:index_filename) ? entry.index_filename : nil
54
+ index_fn = entry.index_filename
55
55
  path = if index_fn
56
56
  File.join(@manifest.root, "zones", entry.path, *remaining, index_fn)
57
57
  else
@@ -71,14 +71,14 @@ module Textus
71
71
  base = File.join(@manifest.root, "zones", entry.path)
72
72
  return [] unless File.directory?(base)
73
73
 
74
- entry_index_filename = entry.respond_to?(:index_filename) ? entry.index_filename : nil
74
+ entry_index_filename = entry.index_filename
75
75
  glob_pattern = entry_index_filename ? "**/#{entry_index_filename}" : nested_glob(entry.format)
76
76
  Dir.glob(File.join(base, glob_pattern)).filter_map { |path| nested_row_for(entry, base, path) }
77
77
  end
78
78
 
79
79
  def nested_row_for(entry, base, path)
80
80
  rel = path.sub(%r{\A#{Regexp.escape(base)}/?}, "")
81
- entry_if = entry.respond_to?(:index_filename) ? entry.index_filename : nil
81
+ entry_if = entry.index_filename
82
82
  stripped = entry_if ? File.dirname(rel) : rel.sub(/#{Regexp.escape(File.extname(rel))}\z/, "")
83
83
  segs = stripped.split("/").reject { |s| s.empty? || s == "." }
84
84
  return nil if segs.empty?
@@ -1,14 +1,14 @@
1
1
  module Textus
2
2
  class Manifest
3
3
  module Schema
4
- ROOT_KEYS = %w[version roles zones entries rules].freeze
4
+ ROOT_KEYS = %w[version roles zones entries rules audit].freeze
5
5
  ROLE_KEYS = %w[name kind].freeze
6
6
  ROLE_KINDS = %w[accept_authority generator proposer runner].freeze
7
7
  ZONE_KEYS = %w[name write_policy read_policy].freeze
8
8
  ENTRY_KEYS = %w[
9
9
  key path zone kind schema owner nested format
10
10
  compute template publish_to publish_each
11
- intake events inject_intro index_filename
11
+ intake events inject_boot index_filename
12
12
  ].freeze
13
13
  COMPUTE_KEYS = %w[kind select pluck sort_by limit transform command sources].freeze
14
14
  INTAKE_KEYS = %w[handler config].freeze
@@ -16,22 +16,37 @@ module Textus
16
16
  REFRESH_KEYS = %w[ttl on_stale sync_budget_ms fetch_timeout_seconds].freeze
17
17
  FETCH_TIMEOUT_SECONDS_CEILING = 3600
18
18
  PROMOTION_KEYS = %w[requires].freeze
19
+ AUDIT_KEYS = %w[max_size keep].freeze
19
20
 
20
21
  def self.validate!(raw)
21
22
  raise BadManifest.new("manifest must be a hash") unless raw.is_a?(Hash)
22
23
 
23
24
  walk(raw, ROOT_KEYS, "$")
24
25
  validate_roles!(raw["roles"])
25
- Array(raw["zones"]).each_with_index do |z, i|
26
+ validate_zones!(raw["zones"])
27
+ validate_entries!(raw["entries"])
28
+ validate_rules!(raw["rules"])
29
+ walk(raw["audit"], AUDIT_KEYS, "$.audit") if raw["audit"].is_a?(Hash)
30
+ validate_zone_writers_declared!(raw)
31
+ end
32
+
33
+ def self.validate_zones!(zones)
34
+ Array(zones).each_with_index do |z, i|
26
35
  walk(z, ZONE_KEYS, "$.zones[#{i}]")
27
36
  end
28
- Array(raw["entries"]).each_with_index do |e, i|
37
+ end
38
+
39
+ def self.validate_entries!(entries)
40
+ Array(entries).each_with_index do |e, i|
29
41
  path = "$.entries[#{i}]"
30
42
  walk(e, ENTRY_KEYS, path)
31
43
  walk(e["compute"], COMPUTE_KEYS, "#{path}.compute") if e["compute"].is_a?(Hash)
32
44
  walk(e["intake"], INTAKE_KEYS, "#{path}.intake") if e["intake"].is_a?(Hash)
33
45
  end
34
- Array(raw["rules"]).each_with_index do |r, i|
46
+ end
47
+
48
+ def self.validate_rules!(rules)
49
+ Array(rules).each_with_index do |r, i|
35
50
  path = "$.rules[#{i}]"
36
51
  walk(r, RULE_KEYS, path)
37
52
  if r["refresh"].is_a?(Hash)
@@ -40,7 +55,6 @@ module Textus
40
55
  end
41
56
  walk(r["promotion"], PROMOTION_KEYS, "#{path}.promotion") if r["promotion"].is_a?(Hash)
42
57
  end
43
- validate_zone_writers_declared!(raw)
44
58
  end
45
59
 
46
60
  def self.validate_zone_writers_declared!(raw)
@@ -30,6 +30,16 @@ module Textus
30
30
  )
31
31
  end
32
32
 
33
+ AUDIT_DEFAULTS = { max_size: 10_485_760, keep: 5 }.freeze
34
+
35
+ def audit_config
36
+ raw = @raw["audit"] || {}
37
+ {
38
+ max_size: raw["max_size"] || AUDIT_DEFAULTS[:max_size],
39
+ keep: raw["keep"] || AUDIT_DEFAULTS[:keep],
40
+ }
41
+ end
42
+
33
43
  def role_mapping
34
44
  @role_mapping ||= RoleKinds.resolve(@raw["roles"])
35
45
  end
@@ -115,11 +115,18 @@ module Textus
115
115
  def rdeps(...) = Application::Reads::Rdeps.new(manifest: @manifest).call(...)
116
116
  def published(...) = Application::Reads::Published.new(manifest: @manifest).call(...)
117
117
  def stale(...) = Application::Reads::Stale.new(manifest: @manifest).call(...)
118
- def audit(...) = Application::Reads::Audit.new(manifest: @manifest, root: @root).call(...)
118
+ def audit(...) = Application::Reads::Audit.new(manifest: @manifest, root: @root, audit_log: @audit_log).call(...)
119
119
  def blame(...) = Application::Reads::Blame.new(manifest: @manifest, root: @root).call(...)
120
120
  def policy_explain(...) = Application::Reads::PolicyExplain.new(manifest: @manifest).call(...)
121
121
  def freshness(...) = Application::Reads::Freshness.new(ctx: @ctx, manifest: @manifest, file_store: @file_store).call(...)
122
122
 
123
+ def pulse(...)
124
+ Application::Reads::Pulse.new(
125
+ ctx: @ctx, manifest: @manifest, file_store: @file_store,
126
+ audit_log: @audit_log, root: @root, store: @store
127
+ ).call(...)
128
+ end
129
+
123
130
  def validate_all(...)
124
131
  Application::Reads::ValidateAll.new(
125
132
  ctx: @ctx, manifest: @manifest, file_store: @file_store, schemas: @schemas, audit_log: @audit_log,
data/lib/textus/store.rb CHANGED
@@ -33,7 +33,11 @@ module Textus
33
33
  @manifest = Manifest.load(@root)
34
34
  @schemas = Schemas.new(File.join(@root, "schemas"))
35
35
  @file_store = Infra::Storage::FileStore.new
36
- @audit_log = Infra::AuditLog.new(@root)
36
+ @audit_log = Infra::AuditLog.new(
37
+ @root,
38
+ max_size: @manifest.audit_config[:max_size],
39
+ keep: @manifest.audit_config[:keep],
40
+ )
37
41
  @bus = Hooks::Bus.new
38
42
  Infra::AuditSubscriber.new(@audit_log).attach(@bus)
39
43
  Hooks::Builtin.register_all(@bus)
@@ -1,4 +1,4 @@
1
1
  module Textus
2
- VERSION = "0.20.2"
2
+ VERSION = "0.22.0"
3
3
  PROTOCOL = "textus/3"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.2
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick
@@ -123,6 +123,7 @@ files:
123
123
  - lib/textus/application/reads/list.rb
124
124
  - lib/textus/application/reads/policy_explain.rb
125
125
  - lib/textus/application/reads/published.rb
126
+ - lib/textus/application/reads/pulse.rb
126
127
  - lib/textus/application/reads/rdeps.rb
127
128
  - lib/textus/application/reads/schema_envelope.rb
128
129
  - lib/textus/application/reads/stale.rb
@@ -142,6 +143,7 @@ files:
142
143
  - lib/textus/application/writes/publish.rb
143
144
  - lib/textus/application/writes/put.rb
144
145
  - lib/textus/application/writes/reject.rb
146
+ - lib/textus/boot.rb
145
147
  - lib/textus/builder/pipeline.rb
146
148
  - lib/textus/builder/renderer.rb
147
149
  - lib/textus/builder/renderer/json.rb
@@ -159,6 +161,7 @@ files:
159
161
  - lib/textus/cli/verb/accept.rb
160
162
  - lib/textus/cli/verb/audit.rb
161
163
  - lib/textus/cli/verb/blame.rb
164
+ - lib/textus/cli/verb/boot.rb
162
165
  - lib/textus/cli/verb/build.rb
163
166
  - lib/textus/cli/verb/delete.rb
164
167
  - lib/textus/cli/verb/deps.rb
@@ -168,10 +171,10 @@ files:
168
171
  - lib/textus/cli/verb/hook_run.rb
169
172
  - lib/textus/cli/verb/hooks.rb
170
173
  - lib/textus/cli/verb/init.rb
171
- - lib/textus/cli/verb/intro.rb
172
174
  - lib/textus/cli/verb/list.rb
173
175
  - lib/textus/cli/verb/mv.rb
174
176
  - lib/textus/cli/verb/published.rb
177
+ - lib/textus/cli/verb/pulse.rb
175
178
  - lib/textus/cli/verb/put.rb
176
179
  - lib/textus/cli/verb/rdeps.rb
177
180
  - lib/textus/cli/verb/refresh.rb
@@ -242,7 +245,6 @@ files:
242
245
  - lib/textus/infra/refresh/lock.rb
243
246
  - lib/textus/infra/storage/file_store.rb
244
247
  - lib/textus/init.rb
245
- - lib/textus/intro.rb
246
248
  - lib/textus/key/distance.rb
247
249
  - lib/textus/key/grammar.rb
248
250
  - lib/textus/key/path.rb
@@ -258,7 +260,7 @@ files:
258
260
  - lib/textus/manifest/entry/validators/events.rb
259
261
  - lib/textus/manifest/entry/validators/format_matrix.rb
260
262
  - lib/textus/manifest/entry/validators/index_filename.rb
261
- - lib/textus/manifest/entry/validators/inject_intro.rb
263
+ - lib/textus/manifest/entry/validators/inject_boot.rb
262
264
  - lib/textus/manifest/entry/validators/publish_each.rb
263
265
  - lib/textus/manifest/resolver.rb
264
266
  - lib/textus/manifest/role_kinds.rb
@@ -1,21 +0,0 @@
1
- module Textus
2
- class Manifest
3
- class Entry
4
- module Validators
5
- module InjectIntro
6
- def self.call(entry)
7
- inject_intro = entry.respond_to?(:inject_intro) ? entry.inject_intro : false
8
- return unless inject_intro
9
-
10
- raise UsageError.new("entry '#{entry.key}': inject_intro: is only valid on derived entries") unless entry.in_generator_zone?
11
-
12
- has_template = entry.respond_to?(:template) && !entry.template.nil?
13
- return if has_template
14
-
15
- raise UsageError.new("entry '#{entry.key}': inject_intro: requires a template:")
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end