textus 0.10.4 → 0.12.1
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/CHANGELOG.md +128 -3
- data/README.md +45 -86
- data/SPEC.md +266 -138
- data/docs/conventions.md +47 -15
- data/lib/textus/application/reads/freshness.rb +2 -2
- data/lib/textus/application/reads/get.rb +1 -1
- data/lib/textus/application/reads/policy_explain.rb +2 -2
- data/lib/textus/application/refresh/orchestrator.rb +1 -1
- data/lib/textus/application/refresh/worker.rb +5 -5
- data/lib/textus/application/writes/accept.rb +19 -1
- data/lib/textus/application/writes/build.rb +5 -5
- data/lib/textus/application/writes/delete.rb +2 -3
- data/lib/textus/application/writes/publish.rb +1 -1
- data/lib/textus/application/writes/put.rb +3 -6
- data/lib/textus/builder/pipeline.rb +1 -1
- data/lib/textus/builder/renderer/json.rb +1 -1
- data/lib/textus/builder/renderer/yaml.rb +1 -1
- data/lib/textus/cli/group/key.rb +1 -1
- data/lib/textus/cli/group/refresh.rb +21 -0
- data/lib/textus/cli/group/rule.rb +11 -0
- data/lib/textus/cli/verb/build.rb +1 -1
- data/lib/textus/cli/verb/hook_run.rb +3 -2
- data/lib/textus/cli/verb/hooks.rb +1 -1
- data/lib/textus/cli/verb/{migrate_keys.rb → key_normalize.rb} +1 -1
- data/lib/textus/cli/verb/put.rb +1 -1
- data/lib/textus/cli/verb/{policy_explain.rb → rule_explain.rb} +1 -1
- data/lib/textus/cli/verb/{policy_list.rb → rule_list.rb} +3 -3
- data/lib/textus/cli/verb.rb +3 -2
- data/lib/textus/cli.rb +6 -6
- data/lib/textus/doctor/check/handler_allowlist.rb +1 -1
- data/lib/textus/doctor/check/illegal_keys.rb +39 -16
- data/lib/textus/doctor/check/intake_registration.rb +4 -4
- data/lib/textus/doctor/check/protocol_version.rb +47 -0
- data/lib/textus/doctor/check/{policy_ambiguity.rb → rule_ambiguity.rb} +6 -6
- data/lib/textus/doctor.rb +5 -4
- data/lib/textus/domain/permission.rb +4 -4
- data/lib/textus/domain/policy/predicates/human_accept.rb +31 -0
- data/lib/textus/domain/policy/predicates/schema_valid.rb +50 -0
- data/lib/textus/domain/policy/promotion.rb +45 -0
- data/lib/textus/errors.rb +24 -5
- data/lib/textus/hooks/builtin.rb +5 -5
- data/lib/textus/hooks/dispatcher.rb +1 -1
- data/lib/textus/hooks/dsl.rb +3 -10
- data/lib/textus/hooks/loader.rb +1 -2
- data/lib/textus/hooks/registry.rb +22 -21
- data/lib/textus/infra/refresh/detached.rb +1 -1
- data/lib/textus/init.rb +25 -34
- data/lib/textus/intro.rb +9 -9
- data/lib/textus/manifest/entry.rb +66 -6
- data/lib/textus/manifest/{policies.rb → rules.rb} +12 -10
- data/lib/textus/manifest/schema.rb +49 -0
- data/lib/textus/manifest.rb +79 -39
- data/lib/textus/migrate_keys.rb +1 -1
- data/lib/textus/projection.rb +4 -4
- data/lib/textus/refresh.rb +1 -1
- data/lib/textus/store/mover.rb +91 -50
- data/lib/textus/store/staleness/generator_check.rb +88 -0
- data/lib/textus/store/staleness/intake_check.rb +46 -0
- data/lib/textus/store/staleness.rb +9 -104
- data/lib/textus/store/writer.rb +14 -12
- data/lib/textus/store.rb +1 -1
- data/lib/textus/version.rb +2 -2
- data/lib/textus.rb +1 -0
- metadata +15 -7
- data/lib/textus/cli/group/policy.rb +0 -11
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "time"
|
|
2
|
+
|
|
3
|
+
module Textus
|
|
4
|
+
class Store
|
|
5
|
+
class Staleness
|
|
6
|
+
# Reports staleness for generator-zone entries — derived files whose
|
|
7
|
+
# generator's listed sources have been modified more recently than the
|
|
8
|
+
# entry's `_meta.generated.at` timestamp. Returns an Array of row hashes
|
|
9
|
+
# (possibly empty) per entry.
|
|
10
|
+
class GeneratorCheck
|
|
11
|
+
def initialize(manifest:)
|
|
12
|
+
@manifest = manifest
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def rows_for(mentry)
|
|
16
|
+
return [] unless mentry.in_generator_zone?
|
|
17
|
+
|
|
18
|
+
gen = mentry.generator
|
|
19
|
+
return [] unless gen
|
|
20
|
+
|
|
21
|
+
path = Textus::Key::Path.resolve(@manifest, mentry)
|
|
22
|
+
return [stale_row(mentry, path, "derived entry has never been generated")] unless File.exist?(path)
|
|
23
|
+
|
|
24
|
+
parsed = Entry.for_format(mentry.format).parse(File.binread(path), path: path)
|
|
25
|
+
generated_at = parsed["_meta"].dig("generated", "at")
|
|
26
|
+
return [stale_row(mentry, path, "missing generated.at frontmatter")] unless generated_at
|
|
27
|
+
|
|
28
|
+
gen_time = parse_time(generated_at)
|
|
29
|
+
return [stale_row(mentry, path, "unparseable generated.at: #{generated_at.inspect}")] unless gen_time
|
|
30
|
+
|
|
31
|
+
offender = newest_source_after(gen, gen_time)
|
|
32
|
+
return [stale_row(mentry, path, "source '#{offender}' modified after generated.at")] if offender
|
|
33
|
+
|
|
34
|
+
[]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def parse_time(str)
|
|
40
|
+
Time.parse(str.to_s)
|
|
41
|
+
rescue StandardError
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def newest_source_after(gen, gen_time)
|
|
46
|
+
Array(gen["sources"]).each do |src|
|
|
47
|
+
offender = check_source(src, gen_time)
|
|
48
|
+
return offender if offender
|
|
49
|
+
end
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_source(src, gen_time)
|
|
54
|
+
if src.match?(/\A[a-z0-9.][a-z0-9._-]*\z/) && !src.include?("/")
|
|
55
|
+
@manifest.enumerate(prefix: src).each do |row|
|
|
56
|
+
return src if File.mtime(row[:path]) > gen_time
|
|
57
|
+
end
|
|
58
|
+
nil
|
|
59
|
+
else
|
|
60
|
+
check_filesystem_source(src, gen_time)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def check_filesystem_source(src, gen_time)
|
|
65
|
+
abs = File.absolute_path?(src) ? src : File.join(File.dirname(@manifest.root), src)
|
|
66
|
+
if File.directory?(abs)
|
|
67
|
+
Dir.glob(File.join(abs, "**", "*")).each do |fp|
|
|
68
|
+
next unless File.file?(fp)
|
|
69
|
+
return src if File.mtime(fp) > gen_time
|
|
70
|
+
end
|
|
71
|
+
nil
|
|
72
|
+
elsif File.exist?(abs) && File.mtime(abs) > gen_time
|
|
73
|
+
src
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def stale_row(mentry, path, reason)
|
|
78
|
+
{
|
|
79
|
+
"key" => mentry.key,
|
|
80
|
+
"path" => path,
|
|
81
|
+
"generator" => mentry.generator,
|
|
82
|
+
"reason" => reason,
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require "time"
|
|
2
|
+
|
|
3
|
+
module Textus
|
|
4
|
+
class Store
|
|
5
|
+
class Staleness
|
|
6
|
+
# Reports TTL-exceeded staleness for intake-handler entries. Returns an
|
|
7
|
+
# Array of row hashes (possibly empty) per entry.
|
|
8
|
+
class IntakeCheck
|
|
9
|
+
def initialize(manifest:)
|
|
10
|
+
@manifest = manifest
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def rows_for(mentry)
|
|
14
|
+
return [] unless mentry.intake_handler
|
|
15
|
+
|
|
16
|
+
ttl = @manifest.rules_for(mentry.key).refresh&.ttl_seconds
|
|
17
|
+
return [] unless ttl
|
|
18
|
+
|
|
19
|
+
path = Textus::Key::Path.resolve(@manifest, mentry)
|
|
20
|
+
return [row(mentry, path, "never refreshed")] unless File.exist?(path)
|
|
21
|
+
|
|
22
|
+
meta = Entry.for_format(mentry.format).parse(File.binread(path), path: path)["_meta"]
|
|
23
|
+
last_str = meta["last_refreshed_at"]
|
|
24
|
+
return [row(mentry, path, "never refreshed (no last_refreshed_at)")] if last_str.nil?
|
|
25
|
+
|
|
26
|
+
last = parse_time(last_str)
|
|
27
|
+
return [row(mentry, path, "ttl exceeded (#{ttl}s)")] if last.nil? || (Time.now - last) > ttl
|
|
28
|
+
|
|
29
|
+
[]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def parse_time(str)
|
|
35
|
+
Time.parse(str.to_s)
|
|
36
|
+
rescue StandardError
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def row(mentry, path, reason)
|
|
41
|
+
{ "key" => mentry.key, "path" => path, "handler" => mentry.intake_handler, "reason" => reason }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -1,121 +1,26 @@
|
|
|
1
|
-
require "time"
|
|
2
|
-
|
|
3
1
|
module Textus
|
|
4
2
|
class Store
|
|
5
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
|
|
6
3
|
class Staleness
|
|
7
4
|
def initialize(manifest:)
|
|
8
5
|
@manifest = manifest
|
|
6
|
+
@generator_check = GeneratorCheck.new(manifest: manifest)
|
|
7
|
+
@intake_check = IntakeCheck.new(manifest: manifest)
|
|
9
8
|
end
|
|
10
9
|
|
|
11
10
|
def call(prefix: nil, zone: nil)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
next if zone && mentry.zone != zone
|
|
16
|
-
|
|
17
|
-
gen = mentry.generator
|
|
18
|
-
next unless gen
|
|
19
|
-
next if prefix && !(mentry.key == prefix || mentry.key.start_with?("#{prefix}."))
|
|
20
|
-
|
|
21
|
-
path = Textus::Key::Path.resolve(@manifest, mentry)
|
|
22
|
-
|
|
23
|
-
unless File.exist?(path)
|
|
24
|
-
out << stale_row(mentry, path, "derived entry has never been generated")
|
|
25
|
-
next
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
raw = File.binread(path)
|
|
29
|
-
parsed = Entry.for_format(mentry.format).parse(raw, path: path)
|
|
30
|
-
generated_at = parsed["_meta"].dig("generated", "at")
|
|
31
|
-
unless generated_at
|
|
32
|
-
out << stale_row(mentry, path, "missing generated.at frontmatter")
|
|
33
|
-
next
|
|
34
|
-
end
|
|
35
|
-
gen_time = begin
|
|
36
|
-
Time.parse(generated_at.to_s)
|
|
37
|
-
rescue StandardError
|
|
38
|
-
nil
|
|
39
|
-
end
|
|
40
|
-
unless gen_time
|
|
41
|
-
out << stale_row(mentry, path, "unparseable generated.at: #{generated_at.inspect}")
|
|
42
|
-
next
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
offender = newest_source_after(gen, gen_time)
|
|
46
|
-
out << stale_row(mentry, path, "source '#{offender}' modified after generated.at") if offender
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
@manifest.entries.each do |mentry|
|
|
50
|
-
next unless mentry.intake_handler
|
|
51
|
-
next if zone && mentry.zone != zone
|
|
52
|
-
next if prefix && !(mentry.key == prefix || mentry.key.start_with?("#{prefix}."))
|
|
53
|
-
|
|
54
|
-
policy_set = @manifest.policies_for(mentry.key)
|
|
55
|
-
ttl = policy_set.refresh&.ttl_seconds
|
|
56
|
-
next unless ttl
|
|
57
|
-
|
|
58
|
-
path = Textus::Key::Path.resolve(@manifest, mentry)
|
|
59
|
-
|
|
60
|
-
unless File.exist?(path)
|
|
61
|
-
out << intake_stale_row(mentry, path, "never refreshed")
|
|
62
|
-
next
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
meta = Entry.for_format(mentry.format).parse(File.binread(path), path: path)["_meta"]
|
|
66
|
-
last_str = meta["last_refreshed_at"]
|
|
67
|
-
if last_str.nil?
|
|
68
|
-
out << intake_stale_row(mentry, path, "never refreshed (no last_refreshed_at)")
|
|
69
|
-
next
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
last = begin
|
|
73
|
-
Time.parse(last_str.to_s)
|
|
74
|
-
rescue StandardError
|
|
75
|
-
nil
|
|
76
|
-
end
|
|
77
|
-
out << intake_stale_row(mentry, path, "ttl exceeded (#{ttl}s)") if last.nil? || (Time.now - last) > ttl
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
out
|
|
11
|
+
@manifest.entries
|
|
12
|
+
.select { |m| entry_matches?(m, prefix: prefix, zone: zone) }
|
|
13
|
+
.flat_map { |m| @generator_check.rows_for(m) + @intake_check.rows_for(m) }
|
|
81
14
|
end
|
|
82
15
|
|
|
83
16
|
private
|
|
84
17
|
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@manifest.enumerate(prefix: src).each do |row|
|
|
89
|
-
return src if File.mtime(row[:path]) > gen_time
|
|
90
|
-
end
|
|
91
|
-
else
|
|
92
|
-
abs = File.absolute_path?(src) ? src : File.join(File.dirname(@manifest.root), src)
|
|
93
|
-
if File.directory?(abs)
|
|
94
|
-
Dir.glob(File.join(abs, "**", "*")).each do |fp|
|
|
95
|
-
next unless File.file?(fp)
|
|
96
|
-
return src if File.mtime(fp) > gen_time
|
|
97
|
-
end
|
|
98
|
-
elsif File.exist?(abs)
|
|
99
|
-
return src if File.mtime(abs) > gen_time
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
nil
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def intake_stale_row(mentry, path, reason)
|
|
107
|
-
{ "key" => mentry.key, "path" => path, "handler" => mentry.intake_handler, "reason" => reason }
|
|
108
|
-
end
|
|
18
|
+
def entry_matches?(mentry, prefix:, zone:)
|
|
19
|
+
return false if zone && mentry.zone != zone
|
|
20
|
+
return false if prefix && !(mentry.key == prefix || mentry.key.start_with?("#{prefix}."))
|
|
109
21
|
|
|
110
|
-
|
|
111
|
-
{
|
|
112
|
-
"key" => mentry.key,
|
|
113
|
-
"path" => path,
|
|
114
|
-
"generator" => mentry.generator,
|
|
115
|
-
"reason" => reason,
|
|
116
|
-
}
|
|
22
|
+
true
|
|
117
23
|
end
|
|
118
24
|
end
|
|
119
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
|
|
120
25
|
end
|
|
121
26
|
end
|
data/lib/textus/store/writer.rb
CHANGED
|
@@ -2,8 +2,9 @@ require "fileutils"
|
|
|
2
2
|
|
|
3
3
|
module Textus
|
|
4
4
|
class Store
|
|
5
|
-
# rubocop:disable Metrics/ParameterLists
|
|
6
5
|
class Writer
|
|
6
|
+
Payload = Data.define(:meta, :body, :content)
|
|
7
|
+
|
|
7
8
|
def initialize(store)
|
|
8
9
|
@store = store
|
|
9
10
|
@manifest = store.manifest
|
|
@@ -11,28 +12,30 @@ module Textus
|
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
# Backward-compat shim — orchestration now lives in Application::Writes::Put.
|
|
15
|
+
# rubocop:disable Metrics/ParameterLists
|
|
14
16
|
def put(key, meta: nil, body: nil, content: nil, if_etag: nil, as: Role::DEFAULT, suppress_events: false)
|
|
15
17
|
ctx = Textus::Application::Context.new(store: @store, role: as)
|
|
16
18
|
Textus::Application::Writes::Put.new(ctx: ctx, bus: @store.bus).call(
|
|
17
19
|
key, meta: meta, body: body, content: content, if_etag: if_etag, suppress_events: suppress_events
|
|
18
20
|
)
|
|
19
21
|
end
|
|
22
|
+
# rubocop:enable Metrics/ParameterLists
|
|
20
23
|
|
|
21
24
|
# Pure I/O: validate, serialize, etag-check, write to disk, audit. No
|
|
22
25
|
# permission check and no event firing — those are handled by the caller
|
|
23
26
|
# (Application::Writes::Put).
|
|
24
|
-
def write_envelope_to_disk(key, mentry:,
|
|
27
|
+
def write_envelope_to_disk(key, mentry:, payload:, ctx:, if_etag: nil)
|
|
25
28
|
_, path, = @manifest.resolve(key)
|
|
26
29
|
|
|
27
|
-
meta
|
|
30
|
+
meta = payload.meta || {}
|
|
28
31
|
strategy = Entry.for_format(mentry.format)
|
|
29
32
|
|
|
30
33
|
existing_uid = existing_uid_for(mentry, path)
|
|
31
|
-
meta, content = ensure_uid(mentry.format, meta, content, existing_uid)
|
|
34
|
+
meta, content = ensure_uid(mentry.format, meta, payload.content, existing_uid)
|
|
32
35
|
|
|
33
36
|
bytes, eff_meta, eff_body, eff_content = serialize_for_put(
|
|
34
37
|
mentry: mentry, path: path, strategy: strategy,
|
|
35
|
-
meta: meta, body: body, content: content
|
|
38
|
+
meta: meta, body: payload.body, content: content
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
enforce_name_match!(path, eff_meta, mentry.format)
|
|
@@ -52,9 +55,9 @@ module Textus
|
|
|
52
55
|
File.binwrite(path, bytes)
|
|
53
56
|
etag_after = Etag.for_bytes(bytes)
|
|
54
57
|
@store.audit_log.append(
|
|
55
|
-
role:
|
|
58
|
+
role: ctx.role, verb: "put", key: key,
|
|
56
59
|
etag_before: etag_before, etag_after: etag_after,
|
|
57
|
-
extras: correlation_id ? { "correlation_id" => correlation_id } : nil
|
|
60
|
+
extras: ctx.correlation_id ? { "correlation_id" => ctx.correlation_id } : nil
|
|
58
61
|
)
|
|
59
62
|
Envelope.build(
|
|
60
63
|
key: key, mentry: mentry, path: path,
|
|
@@ -129,7 +132,7 @@ module Textus
|
|
|
129
132
|
# Pure I/O: resolve path, validate etag, delete from disk, audit. No
|
|
130
133
|
# permission check and no event firing — those are handled by the caller
|
|
131
134
|
# (Application::Writes::Delete).
|
|
132
|
-
def delete_envelope_from_disk(key, if_etag: nil
|
|
135
|
+
def delete_envelope_from_disk(key, ctx:, if_etag: nil)
|
|
133
136
|
_, path, = @manifest.resolve(key)
|
|
134
137
|
raise UnknownKey.new(key, suggestions: @manifest.suggestions_for(key)) unless File.exist?(path)
|
|
135
138
|
|
|
@@ -138,9 +141,9 @@ module Textus
|
|
|
138
141
|
|
|
139
142
|
File.delete(path)
|
|
140
143
|
@store.audit_log.append(
|
|
141
|
-
role:
|
|
144
|
+
role: ctx.role, verb: "delete", key: key,
|
|
142
145
|
etag_before: etag_before, etag_after: nil,
|
|
143
|
-
extras: correlation_id ? { "correlation_id" => correlation_id } : nil
|
|
146
|
+
extras: ctx.correlation_id ? { "correlation_id" => ctx.correlation_id } : nil
|
|
144
147
|
)
|
|
145
148
|
end
|
|
146
149
|
|
|
@@ -160,10 +163,9 @@ module Textus
|
|
|
160
163
|
target_key = proposal["target_key"] or raise ProposalError.new("proposal missing target_key")
|
|
161
164
|
|
|
162
165
|
delete(pending_key, as: as)
|
|
163
|
-
@store.fire_event(:
|
|
166
|
+
@store.fire_event(:proposal_rejected, key: pending_key, target_key: target_key)
|
|
164
167
|
{ "protocol" => PROTOCOL, "rejected" => pending_key, "target_key" => target_key }
|
|
165
168
|
end
|
|
166
169
|
end
|
|
167
|
-
# rubocop:enable Metrics/ParameterLists
|
|
168
170
|
end
|
|
169
171
|
end
|
data/lib/textus/store.rb
CHANGED
data/lib/textus/version.rb
CHANGED
data/lib/textus.rb
CHANGED
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.
|
|
4
|
+
version: 0.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick
|
|
@@ -133,7 +133,8 @@ files:
|
|
|
133
133
|
- lib/textus/cli/group.rb
|
|
134
134
|
- lib/textus/cli/group/hook.rb
|
|
135
135
|
- lib/textus/cli/group/key.rb
|
|
136
|
-
- lib/textus/cli/group/
|
|
136
|
+
- lib/textus/cli/group/refresh.rb
|
|
137
|
+
- lib/textus/cli/group/rule.rb
|
|
137
138
|
- lib/textus/cli/group/schema.rb
|
|
138
139
|
- lib/textus/cli/verb.rb
|
|
139
140
|
- lib/textus/cli/verb/accept.rb
|
|
@@ -149,17 +150,17 @@ files:
|
|
|
149
150
|
- lib/textus/cli/verb/hooks.rb
|
|
150
151
|
- lib/textus/cli/verb/init.rb
|
|
151
152
|
- lib/textus/cli/verb/intro.rb
|
|
153
|
+
- lib/textus/cli/verb/key_normalize.rb
|
|
152
154
|
- lib/textus/cli/verb/list.rb
|
|
153
|
-
- lib/textus/cli/verb/migrate_keys.rb
|
|
154
155
|
- lib/textus/cli/verb/mv.rb
|
|
155
|
-
- lib/textus/cli/verb/policy_explain.rb
|
|
156
|
-
- lib/textus/cli/verb/policy_list.rb
|
|
157
156
|
- lib/textus/cli/verb/published.rb
|
|
158
157
|
- lib/textus/cli/verb/put.rb
|
|
159
158
|
- lib/textus/cli/verb/rdeps.rb
|
|
160
159
|
- lib/textus/cli/verb/refresh.rb
|
|
161
160
|
- lib/textus/cli/verb/refresh_stale.rb
|
|
162
161
|
- lib/textus/cli/verb/reject.rb
|
|
162
|
+
- lib/textus/cli/verb/rule_explain.rb
|
|
163
|
+
- lib/textus/cli/verb/rule_list.rb
|
|
163
164
|
- lib/textus/cli/verb/schema.rb
|
|
164
165
|
- lib/textus/cli/verb/schema_diff.rb
|
|
165
166
|
- lib/textus/cli/verb/schema_init.rb
|
|
@@ -176,7 +177,8 @@ files:
|
|
|
176
177
|
- lib/textus/doctor/check/illegal_keys.rb
|
|
177
178
|
- lib/textus/doctor/check/intake_registration.rb
|
|
178
179
|
- lib/textus/doctor/check/manifest_files.rb
|
|
179
|
-
- lib/textus/doctor/check/
|
|
180
|
+
- lib/textus/doctor/check/protocol_version.rb
|
|
181
|
+
- lib/textus/doctor/check/rule_ambiguity.rb
|
|
180
182
|
- lib/textus/doctor/check/schema_parse_error.rb
|
|
181
183
|
- lib/textus/doctor/check/schema_violations.rb
|
|
182
184
|
- lib/textus/doctor/check/schemas.rb
|
|
@@ -192,7 +194,10 @@ files:
|
|
|
192
194
|
- lib/textus/domain/policy.rb
|
|
193
195
|
- lib/textus/domain/policy/handler_allowlist.rb
|
|
194
196
|
- lib/textus/domain/policy/matcher.rb
|
|
197
|
+
- lib/textus/domain/policy/predicates/human_accept.rb
|
|
198
|
+
- lib/textus/domain/policy/predicates/schema_valid.rb
|
|
195
199
|
- lib/textus/domain/policy/promote.rb
|
|
200
|
+
- lib/textus/domain/policy/promotion.rb
|
|
196
201
|
- lib/textus/domain/policy/refresh.rb
|
|
197
202
|
- lib/textus/entry.rb
|
|
198
203
|
- lib/textus/entry/base.rb
|
|
@@ -220,7 +225,8 @@ files:
|
|
|
220
225
|
- lib/textus/key/path.rb
|
|
221
226
|
- lib/textus/manifest.rb
|
|
222
227
|
- lib/textus/manifest/entry.rb
|
|
223
|
-
- lib/textus/manifest/
|
|
228
|
+
- lib/textus/manifest/rules.rb
|
|
229
|
+
- lib/textus/manifest/schema.rb
|
|
224
230
|
- lib/textus/migrate_keys.rb
|
|
225
231
|
- lib/textus/mustache.rb
|
|
226
232
|
- lib/textus/projection.rb
|
|
@@ -235,6 +241,8 @@ files:
|
|
|
235
241
|
- lib/textus/store/reader.rb
|
|
236
242
|
- lib/textus/store/sentinel.rb
|
|
237
243
|
- lib/textus/store/staleness.rb
|
|
244
|
+
- lib/textus/store/staleness/generator_check.rb
|
|
245
|
+
- lib/textus/store/staleness/intake_check.rb
|
|
238
246
|
- lib/textus/store/validator.rb
|
|
239
247
|
- lib/textus/store/writer.rb
|
|
240
248
|
- lib/textus/version.rb
|