textus 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -1
- data/README.md +22 -18
- data/SPEC.md +49 -35
- data/docs/architecture.md +63 -28
- data/lib/textus/audit_log.rb +46 -11
- data/lib/textus/builder.rb +3 -3
- data/lib/textus/{builtin_fetchers.rb → builtin_actions.rb} +16 -11
- data/lib/textus/cli/accept.rb +13 -0
- data/lib/textus/cli/action.rb +51 -0
- data/lib/textus/cli/build.rb +11 -0
- data/lib/textus/cli/delete.rb +14 -0
- data/lib/textus/cli/deprecated_alias.rb +31 -0
- data/lib/textus/cli/deps.rb +10 -0
- data/lib/textus/cli/doctor.rb +13 -0
- data/lib/textus/cli/extension_group.rb +9 -0
- data/lib/textus/cli/extensions.rb +49 -0
- data/lib/textus/cli/get.rb +10 -0
- data/lib/textus/cli/group.rb +51 -0
- data/lib/textus/cli/init.rb +12 -0
- data/lib/textus/cli/intro.rb +9 -0
- data/lib/textus/cli/key_group.rb +10 -0
- data/lib/textus/cli/list.rb +12 -0
- data/lib/textus/cli/migrate.rb +41 -0
- data/lib/textus/cli/migrate_keys.rb +19 -0
- data/lib/textus/cli/mv.rb +20 -0
- data/lib/textus/cli/published.rb +9 -0
- data/lib/textus/cli/put.rb +48 -0
- data/lib/textus/cli/rdeps.rb +10 -0
- data/lib/textus/cli/refresh.rb +13 -0
- data/lib/textus/cli/schema.rb +10 -0
- data/lib/textus/cli/schema_diff.rb +15 -0
- data/lib/textus/cli/schema_group.rb +33 -0
- data/lib/textus/cli/schema_init.rb +19 -0
- data/lib/textus/cli/schema_migrate.rb +19 -0
- data/lib/textus/cli/stale.rb +12 -0
- data/lib/textus/cli/uid.rb +15 -0
- data/lib/textus/cli/verb.rb +62 -0
- data/lib/textus/cli/where.rb +10 -0
- data/lib/textus/cli.rb +65 -347
- data/lib/textus/doctor.rb +103 -32
- data/lib/textus/entry/json.rb +6 -4
- data/lib/textus/entry/markdown.rb +4 -4
- data/lib/textus/entry/text.rb +3 -3
- data/lib/textus/entry/yaml.rb +6 -4
- data/lib/textus/entry.rb +2 -2
- data/lib/textus/errors.rb +2 -2
- data/lib/textus/extension_registry.rb +22 -9
- data/lib/textus/extensions.rb +6 -2
- data/lib/textus/init.rb +6 -5
- data/lib/textus/intro.rb +11 -9
- data/lib/textus/manifest.rb +11 -215
- data/lib/textus/manifest_entry.rb +185 -0
- data/lib/textus/migrate_v2.rb +27 -0
- data/lib/textus/projection.rb +1 -1
- data/lib/textus/proposal.rb +3 -3
- data/lib/textus/refresh.rb +21 -20
- data/lib/textus/schema_tools.rb +8 -8
- data/lib/textus/store/events.rb +31 -0
- data/lib/textus/store/mover.rb +118 -0
- data/lib/textus/store/staleness.rb +142 -0
- data/lib/textus/store/validator.rb +53 -0
- data/lib/textus/store.rb +50 -355
- data/lib/textus/store_view.rb +11 -2
- data/lib/textus/version.rb +2 -2
- data/lib/textus.rb +39 -1
- metadata +39 -2
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
class CLI
|
|
3
|
+
class SchemaMigrate < Verb
|
|
4
|
+
prepend DeprecatedAliasMixin
|
|
5
|
+
|
|
6
|
+
def self.deprecated_name = "schema-migrate"
|
|
7
|
+
def self.replacement_path = "schema migrate"
|
|
8
|
+
|
|
9
|
+
option :rename, "--rename=O:N"
|
|
10
|
+
|
|
11
|
+
def call(store)
|
|
12
|
+
name = positional.shift or raise UsageError.new("schema-migrate NAME")
|
|
13
|
+
raise UsageError.new("schema-migrate requires --rename=OLD:NEW") unless rename
|
|
14
|
+
|
|
15
|
+
emit(Textus::SchemaTools.migrate(store, name: name, rename: rename))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
class CLI
|
|
3
|
+
class Uid < Verb
|
|
4
|
+
prepend DeprecatedAliasMixin
|
|
5
|
+
|
|
6
|
+
def self.deprecated_name = "uid"
|
|
7
|
+
def self.replacement_path = "key uid"
|
|
8
|
+
|
|
9
|
+
def call(store)
|
|
10
|
+
key = positional.shift or raise UsageError.new("uid requires a key")
|
|
11
|
+
emit({ "key" => key, "uid" => store.uid(key) })
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "optparse"
|
|
3
|
+
|
|
4
|
+
module Textus
|
|
5
|
+
class CLI
|
|
6
|
+
# Subclasses must implement #call(store) and return an integer exit code.
|
|
7
|
+
# Use #emit(obj) for normal JSON output (returns 0).
|
|
8
|
+
# Subclasses that don't need a Textus store (e.g. Init) override
|
|
9
|
+
# `.needs_store?` to return false; dispatch will pass nil instead.
|
|
10
|
+
class Verb
|
|
11
|
+
class << self
|
|
12
|
+
def option(name, optspec)
|
|
13
|
+
options << [name, optspec]
|
|
14
|
+
attr_accessor(name)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def options
|
|
18
|
+
@options ||= []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def needs_store?
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def inherited(subclass)
|
|
26
|
+
super
|
|
27
|
+
subclass.instance_variable_set(:@options, [])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def initialize(stdin:, stdout:, stderr:, cwd: nil)
|
|
32
|
+
@stdin = stdin
|
|
33
|
+
@stdout = stdout
|
|
34
|
+
@stderr = stderr
|
|
35
|
+
@cwd = cwd
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parse(argv)
|
|
39
|
+
fmt = "json"
|
|
40
|
+
OptionParser.new do |o|
|
|
41
|
+
self.class.options.each do |name, optspec|
|
|
42
|
+
o.on(optspec) { |v| public_send(:"#{name}=", v) }
|
|
43
|
+
end
|
|
44
|
+
o.on("--format=FMT") { |v| fmt = v }
|
|
45
|
+
end.permute!(argv)
|
|
46
|
+
raise UsageError.new("only --format=json is supported in v1") unless fmt == "json"
|
|
47
|
+
|
|
48
|
+
@positional = argv.dup
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attr_reader :positional
|
|
52
|
+
|
|
53
|
+
# Hashes get "protocol" => PROTOCOL prepended unless they already
|
|
54
|
+
# carry one (Store envelopes do). Caller's value wins on collision.
|
|
55
|
+
def emit(obj, exit_code: 0)
|
|
56
|
+
payload = obj.is_a?(Hash) ? { "protocol" => PROTOCOL }.merge(obj) : obj
|
|
57
|
+
@stdout.puts(JSON.generate(payload))
|
|
58
|
+
exit_code
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/textus/cli.rb
CHANGED
|
@@ -1,12 +1,45 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require "optparse"
|
|
3
|
-
require "time"
|
|
4
|
-
require "timeout"
|
|
5
|
-
require "yaml"
|
|
6
3
|
|
|
7
4
|
module Textus
|
|
8
|
-
# rubocop:disable Metrics/ClassLength
|
|
9
5
|
class CLI
|
|
6
|
+
# verb name → Verb subclass. Adding a new verb is a one-line entry here
|
|
7
|
+
# plus a new file under lib/textus/cli/.
|
|
8
|
+
VERBS = {
|
|
9
|
+
"accept" => Accept,
|
|
10
|
+
"action" => Action,
|
|
11
|
+
"build" => Build,
|
|
12
|
+
"delete" => Delete,
|
|
13
|
+
"deps" => Deps,
|
|
14
|
+
"doctor" => DoctorVerb,
|
|
15
|
+
"extension" => ExtensionGroup,
|
|
16
|
+
"extensions" => Extensions,
|
|
17
|
+
"get" => Get,
|
|
18
|
+
"init" => InitVerb,
|
|
19
|
+
"intro" => IntroVerb,
|
|
20
|
+
"key" => KeyGroup,
|
|
21
|
+
"list" => List,
|
|
22
|
+
"migrate" => Migrate,
|
|
23
|
+
"migrate-keys" => MigrateKeysVerb,
|
|
24
|
+
"mv" => Mv,
|
|
25
|
+
"published" => Published,
|
|
26
|
+
"put" => Put,
|
|
27
|
+
"rdeps" => Rdeps,
|
|
28
|
+
"refresh" => RefreshVerb,
|
|
29
|
+
"schema" => SchemaGroup,
|
|
30
|
+
"schema-diff" => SchemaDiff,
|
|
31
|
+
"schema-init" => SchemaInit,
|
|
32
|
+
"schema-migrate" => SchemaMigrate,
|
|
33
|
+
"stale" => Stale,
|
|
34
|
+
"uid" => Uid,
|
|
35
|
+
"where" => Where,
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
# Flat aliases kept for backward-compat through 0.5; emit deprecation warnings.
|
|
39
|
+
DEPRECATED_ALIASES = %w[
|
|
40
|
+
mv uid migrate-keys schema-init schema-diff schema-migrate extensions action
|
|
41
|
+
].freeze
|
|
42
|
+
|
|
10
43
|
def self.run(argv, stdin: $stdin, stdout: $stdout, stderr: $stderr, cwd: Dir.pwd)
|
|
11
44
|
new(stdin: stdin, stdout: stdout, stderr: stderr, cwd: cwd).run(argv)
|
|
12
45
|
end
|
|
@@ -19,43 +52,19 @@ module Textus
|
|
|
19
52
|
@root_arg = nil
|
|
20
53
|
end
|
|
21
54
|
|
|
22
|
-
def run(argv)
|
|
23
|
-
OptionParser.new
|
|
24
|
-
o.on("--root=PATH") { |v| @root_arg = v }
|
|
25
|
-
end.order!(argv)
|
|
55
|
+
def run(argv)
|
|
56
|
+
OptionParser.new { |o| o.on("--root=PATH") { |v| @root_arg = v } }.order!(argv)
|
|
26
57
|
verb = argv.shift
|
|
27
58
|
raise UsageError.new("missing verb") if verb.nil?
|
|
28
59
|
|
|
29
60
|
case verb
|
|
30
|
-
when "list" then verb_list(argv)
|
|
31
|
-
when "where" then verb_where(argv)
|
|
32
|
-
when "get" then verb_get(argv)
|
|
33
|
-
when "put" then verb_put(argv)
|
|
34
|
-
when "schema" then verb_schema(argv)
|
|
35
|
-
when "stale" then verb_stale(argv)
|
|
36
|
-
when "delete" then verb_delete(argv)
|
|
37
|
-
when "validate-all" then verb_validate_all(argv)
|
|
38
|
-
when "build" then verb_build(argv)
|
|
39
|
-
when "deps" then verb_deps(argv)
|
|
40
|
-
when "rdeps" then verb_rdeps(argv)
|
|
41
|
-
when "published" then verb_published(argv)
|
|
42
|
-
when "accept" then verb_accept(argv)
|
|
43
|
-
when "init" then verb_init(argv)
|
|
44
|
-
when "schema-init" then verb_schema_init(argv)
|
|
45
|
-
when "schema-diff" then verb_schema_diff(argv)
|
|
46
|
-
when "schema-migrate" then verb_schema_migrate(argv)
|
|
47
|
-
when "refresh" then verb_refresh(argv)
|
|
48
|
-
when "extensions" then verb_extensions(argv)
|
|
49
|
-
when "migrate-keys" then verb_migrate_keys(argv)
|
|
50
|
-
when "mv" then verb_mv(argv)
|
|
51
|
-
when "uid" then verb_uid(argv)
|
|
52
|
-
when "doctor" then verb_doctor(argv)
|
|
53
|
-
when "intro" then verb_intro(argv)
|
|
54
61
|
when "--version", "-v" then @stdout.puts(VERSION)
|
|
55
62
|
0
|
|
56
63
|
when "--help", "-h" then print_help
|
|
57
64
|
0
|
|
58
|
-
else
|
|
65
|
+
else
|
|
66
|
+
klass = VERBS[verb] or raise UsageError.new("unknown verb: #{verb}")
|
|
67
|
+
dispatch(klass, argv, deprecated_alias: DEPRECATED_ALIASES.include?(verb))
|
|
59
68
|
end
|
|
60
69
|
rescue Textus::Error => e
|
|
61
70
|
emit_error(e)
|
|
@@ -67,310 +76,11 @@ module Textus
|
|
|
67
76
|
@store ||= Store.discover(@cwd, root: @root_arg)
|
|
68
77
|
end
|
|
69
78
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
raise UsageError.new("only --format=json is supported in v1") unless fmt == "json"
|
|
76
|
-
|
|
77
|
-
fmt
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def parse_prefix!(argv)
|
|
81
|
-
prefix = nil
|
|
82
|
-
OptionParser.new do |o|
|
|
83
|
-
o.on("--prefix=KEY") { |v| prefix = v }
|
|
84
|
-
o.on("--zone=Z") {}
|
|
85
|
-
o.on("--format=FMT") {}
|
|
86
|
-
end.permute!(argv)
|
|
87
|
-
prefix
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def parse_prefix_and_zone!(argv)
|
|
91
|
-
prefix = nil
|
|
92
|
-
zone = nil
|
|
93
|
-
fmt = "text"
|
|
94
|
-
OptionParser.new do |o|
|
|
95
|
-
o.on("--prefix=KEY") { |v| prefix = v }
|
|
96
|
-
o.on("--zone=Z") { |v| zone = v }
|
|
97
|
-
o.on("--format=FMT") { |v| fmt = v }
|
|
98
|
-
end.permute!(argv)
|
|
99
|
-
raise UsageError.new("only --format=json is supported in v1") unless fmt == "json"
|
|
100
|
-
|
|
101
|
-
[prefix, zone]
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def verb_list(argv)
|
|
105
|
-
prefix, zone = parse_prefix_and_zone!(argv)
|
|
106
|
-
emit({ "protocol" => PROTOCOL, "entries" => store.list(prefix: prefix, zone: zone) })
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def verb_where(argv)
|
|
110
|
-
key = argv.shift or raise UsageError.new("where requires a key")
|
|
111
|
-
parse_format!(argv)
|
|
112
|
-
emit(store.where(key))
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def verb_get(argv)
|
|
116
|
-
key = argv.shift or raise UsageError.new("get requires a key")
|
|
117
|
-
parse_format!(argv)
|
|
118
|
-
emit(store.get(key))
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def verb_schema(argv)
|
|
122
|
-
key = argv.shift or raise UsageError.new("schema requires a key")
|
|
123
|
-
parse_format!(argv)
|
|
124
|
-
emit(store.schema_envelope(key))
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def verb_stale(argv)
|
|
128
|
-
prefix, zone = parse_prefix_and_zone!(argv)
|
|
129
|
-
emit(store.stale(prefix: prefix, zone: zone))
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def verb_put(argv) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
133
|
-
key = argv.shift or raise UsageError.new("put requires a key")
|
|
134
|
-
as_flag = nil
|
|
135
|
-
use_stdin = false
|
|
136
|
-
fetcher_name = nil
|
|
137
|
-
OptionParser.new do |o|
|
|
138
|
-
o.on("--stdin") { use_stdin = true }
|
|
139
|
-
o.on("--as=ROLE") { |v| as_flag = v }
|
|
140
|
-
o.on("--fetcher=NAME") { |v| fetcher_name = v }
|
|
141
|
-
o.on("--format=FMT") {}
|
|
142
|
-
end.permute!(argv)
|
|
143
|
-
raise UsageError.new("put requires --stdin in v1") unless use_stdin
|
|
144
|
-
|
|
145
|
-
role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
|
|
146
|
-
|
|
147
|
-
raw = @stdin.read
|
|
148
|
-
payload =
|
|
149
|
-
if fetcher_name
|
|
150
|
-
callable = store.registry.fetcher(fetcher_name)
|
|
151
|
-
result =
|
|
152
|
-
begin
|
|
153
|
-
Timeout.timeout(Textus::Refresh::FETCHER_TIMEOUT_SECONDS) do
|
|
154
|
-
callable.call(config: { "bytes" => raw }, store: Textus::StoreView.new(store))
|
|
155
|
-
end
|
|
156
|
-
rescue Timeout::Error
|
|
157
|
-
raise UsageError.new(
|
|
158
|
-
"fetcher '#{fetcher_name}' exceeded #{Textus::Refresh::FETCHER_TIMEOUT_SECONDS}s timeout",
|
|
159
|
-
)
|
|
160
|
-
end
|
|
161
|
-
basename = key.split(".").last
|
|
162
|
-
{
|
|
163
|
-
"frontmatter" => {
|
|
164
|
-
"name" => basename,
|
|
165
|
-
"last_refreshed_at" => Time.now.utc.iso8601,
|
|
166
|
-
"fetched_with" => fetcher_name,
|
|
167
|
-
}.merge(result[:frontmatter] || result["frontmatter"] || {}),
|
|
168
|
-
"body" => result[:body] || result["body"] || "",
|
|
169
|
-
}
|
|
170
|
-
else
|
|
171
|
-
JSON.parse(raw)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
fm = payload["frontmatter"] || {}
|
|
175
|
-
body = payload["body"] || ""
|
|
176
|
-
if_etag = payload["if_etag"]
|
|
177
|
-
emit(store.put(key, frontmatter: fm, body: body, if_etag: if_etag, as: role))
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def verb_delete(argv)
|
|
181
|
-
key = argv.shift or raise UsageError.new("delete requires a key")
|
|
182
|
-
as_flag = nil
|
|
183
|
-
if_etag = nil
|
|
184
|
-
OptionParser.new do |o|
|
|
185
|
-
o.on("--as=ROLE") { |v| as_flag = v }
|
|
186
|
-
o.on("--if-etag=E") { |v| if_etag = v }
|
|
187
|
-
o.on("--format=FMT") {}
|
|
188
|
-
end.permute!(argv)
|
|
189
|
-
role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
|
|
190
|
-
emit(store.delete(key, if_etag: if_etag, as: role))
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def verb_validate_all(argv)
|
|
194
|
-
parse_format!(argv)
|
|
195
|
-
res = store.validate_all
|
|
196
|
-
@stdout.puts(JSON.generate(res))
|
|
197
|
-
res["ok"] ? 0 : 1
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def verb_build(argv)
|
|
201
|
-
prefix = nil
|
|
202
|
-
OptionParser.new do |o|
|
|
203
|
-
o.on("--prefix=K") { |v| prefix = v }
|
|
204
|
-
o.on("--format=FMT") {}
|
|
205
|
-
end.permute!(argv)
|
|
206
|
-
res = Textus::Builder.new(store).build(prefix: prefix)
|
|
207
|
-
@stdout.puts(JSON.generate(res))
|
|
208
|
-
0
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def verb_deps(argv)
|
|
212
|
-
key = argv.shift or raise UsageError.new("deps requires a key")
|
|
213
|
-
parse_format!(argv)
|
|
214
|
-
emit({ "protocol" => Textus::PROTOCOL, "key" => key, "deps" => store.deps(key) })
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def verb_rdeps(argv)
|
|
218
|
-
key = argv.shift or raise UsageError.new("rdeps requires a key")
|
|
219
|
-
parse_format!(argv)
|
|
220
|
-
emit({ "protocol" => Textus::PROTOCOL, "key" => key, "rdeps" => store.rdeps(key) })
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def verb_schema_init(argv)
|
|
224
|
-
name = argv.shift or raise UsageError.new("schema-init NAME")
|
|
225
|
-
from_key = nil
|
|
226
|
-
OptionParser.new do |o|
|
|
227
|
-
o.on("--from=KEY") { |v| from_key = v }
|
|
228
|
-
o.on("--format=FMT") {}
|
|
229
|
-
end.permute!(argv)
|
|
230
|
-
raise UsageError.new("schema-init requires --from=KEY") unless from_key
|
|
231
|
-
|
|
232
|
-
emit(Textus::SchemaTools.init(store, name: name, from: from_key))
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def verb_schema_diff(argv)
|
|
236
|
-
name = argv.shift or raise UsageError.new("schema-diff NAME")
|
|
237
|
-
parse_format!(argv)
|
|
238
|
-
emit(Textus::SchemaTools.diff(store, name: name))
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def verb_schema_migrate(argv)
|
|
242
|
-
name = argv.shift or raise UsageError.new("schema-migrate NAME")
|
|
243
|
-
rename = nil
|
|
244
|
-
OptionParser.new do |o|
|
|
245
|
-
o.on("--rename=O:N") { |v| rename = v }
|
|
246
|
-
o.on("--format=FMT") {}
|
|
247
|
-
end.permute!(argv)
|
|
248
|
-
raise UsageError.new("schema-migrate requires --rename=OLD:NEW") unless rename
|
|
249
|
-
|
|
250
|
-
emit(Textus::SchemaTools.migrate(store, name: name, rename: rename))
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def verb_init(argv)
|
|
254
|
-
OptionParser.new do |o|
|
|
255
|
-
o.on("--format=FMT") {}
|
|
256
|
-
end.permute!(argv)
|
|
257
|
-
target = File.join(@cwd, ".textus")
|
|
258
|
-
res = Textus::Init.run(target)
|
|
259
|
-
@stdout.puts(JSON.generate(res))
|
|
260
|
-
0
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def verb_accept(argv)
|
|
264
|
-
key = argv.shift or raise UsageError.new("accept requires a key")
|
|
265
|
-
as_flag = nil
|
|
266
|
-
OptionParser.new do |o|
|
|
267
|
-
o.on("--as=ROLE") { |v| as_flag = v }
|
|
268
|
-
o.on("--format=FMT") {}
|
|
269
|
-
end.permute!(argv)
|
|
270
|
-
role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
|
|
271
|
-
emit(store.accept(key, as: role))
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def verb_refresh(argv)
|
|
275
|
-
key = argv.shift or raise UsageError.new("refresh requires a key")
|
|
276
|
-
as_flag = nil
|
|
277
|
-
OptionParser.new do |o|
|
|
278
|
-
o.on("--as=ROLE") { |v| as_flag = v }
|
|
279
|
-
o.on("--format=FMT") {}
|
|
280
|
-
end.permute!(argv)
|
|
281
|
-
role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
|
|
282
|
-
emit(Textus::Refresh.call(store, key, as: role))
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def verb_extensions(argv) # rubocop:disable Metrics/AbcSize
|
|
286
|
-
subcommand = argv.shift
|
|
287
|
-
raise UsageError.new("extensions requires 'list'") unless subcommand == "list"
|
|
288
|
-
|
|
289
|
-
kind = nil
|
|
290
|
-
OptionParser.new do |o|
|
|
291
|
-
o.on("--kind=K") { |v| kind = v }
|
|
292
|
-
o.on("--format=FMT") {}
|
|
293
|
-
end.permute!(argv)
|
|
294
|
-
|
|
295
|
-
rows = []
|
|
296
|
-
rows += store.registry.fetcher_names.map { |n| { "kind" => "fetcher", "name" => n.to_s } }
|
|
297
|
-
rows += store.registry.reducer_names.map { |n| { "kind" => "reducer", "name" => n.to_s } }
|
|
298
|
-
store.registry.hook_events.each do |evt|
|
|
299
|
-
store.registry.hooks(evt).each do |h|
|
|
300
|
-
rows << { "kind" => "hook", "event" => evt.to_s, "name" => h[:name].to_s }
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
store.manifest.entries.each do |e|
|
|
304
|
-
e.events.each do |evt, defs|
|
|
305
|
-
Array(defs).each do |defn|
|
|
306
|
-
next unless defn["exec"]
|
|
307
|
-
|
|
308
|
-
rows << {
|
|
309
|
-
"kind" => "hook", "event" => evt.to_s, "exec" => defn["exec"],
|
|
310
|
-
"key" => e.key, "as" => defn["as"] || "script"
|
|
311
|
-
}
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
rows.select! { |r| r["kind"] == kind } if kind
|
|
316
|
-
|
|
317
|
-
emit({ "protocol" => Textus::PROTOCOL, "extensions" => rows })
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def verb_migrate_keys(argv)
|
|
321
|
-
write = false
|
|
322
|
-
OptionParser.new do |o|
|
|
323
|
-
o.on("--dry-run") { write = false }
|
|
324
|
-
o.on("--write") { write = true }
|
|
325
|
-
o.on("--format=FMT") {}
|
|
326
|
-
end.permute!(argv)
|
|
327
|
-
res = Textus::MigrateKeys.run(store, write: write)
|
|
328
|
-
@stdout.puts(JSON.generate(res))
|
|
329
|
-
res["ok"] ? 0 : 1
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
def verb_mv(argv)
|
|
333
|
-
old_key = argv.shift or raise UsageError.new("mv requires <old-key> <new-key>")
|
|
334
|
-
new_key = argv.shift or raise UsageError.new("mv requires <old-key> <new-key>")
|
|
335
|
-
as_flag = nil
|
|
336
|
-
dry_run = false
|
|
337
|
-
OptionParser.new do |o|
|
|
338
|
-
o.on("--as=ROLE") { |v| as_flag = v }
|
|
339
|
-
o.on("--dry-run") { dry_run = true }
|
|
340
|
-
o.on("--format=FMT") {}
|
|
341
|
-
end.permute!(argv)
|
|
342
|
-
role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
|
|
343
|
-
emit(store.mv(old_key, new_key, as: role, dry_run: dry_run))
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
def verb_uid(argv)
|
|
347
|
-
key = argv.shift or raise UsageError.new("uid requires a key")
|
|
348
|
-
parse_format!(argv)
|
|
349
|
-
emit({ "protocol" => PROTOCOL, "key" => key, "uid" => store.uid(key) })
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
def verb_intro(argv)
|
|
353
|
-
parse_format!(argv)
|
|
354
|
-
emit(Textus::Intro.run(store))
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
def verb_doctor(argv)
|
|
358
|
-
OptionParser.new do |o|
|
|
359
|
-
o.on("--format=FMT") {}
|
|
360
|
-
end.permute!(argv)
|
|
361
|
-
res = Textus::Doctor.run(store)
|
|
362
|
-
@stdout.puts(JSON.generate(res))
|
|
363
|
-
res["ok"] ? 0 : 1
|
|
364
|
-
end
|
|
365
|
-
|
|
366
|
-
def verb_published(argv)
|
|
367
|
-
parse_format!(argv)
|
|
368
|
-
emit({ "protocol" => Textus::PROTOCOL, "published" => store.published })
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
def emit(obj)
|
|
372
|
-
@stdout.puts(JSON.generate(obj))
|
|
373
|
-
0
|
|
79
|
+
def dispatch(klass, argv, deprecated_alias: false)
|
|
80
|
+
v = klass.new(stdin: @stdin, stdout: @stdout, stderr: @stderr, cwd: @cwd)
|
|
81
|
+
v.deprecated_alias = true if deprecated_alias && v.respond_to?(:deprecated_alias=)
|
|
82
|
+
v.parse(argv)
|
|
83
|
+
v.call(klass.needs_store? ? store : nil)
|
|
374
84
|
end
|
|
375
85
|
|
|
376
86
|
def emit_error(err)
|
|
@@ -382,17 +92,25 @@ module Textus
|
|
|
382
92
|
|
|
383
93
|
def print_help
|
|
384
94
|
@stdout.puts <<~HELP
|
|
385
|
-
textus #{VERSION} — reference implementation of #{PROTOCOL}
|
|
386
|
-
|
|
387
|
-
Usage:
|
|
388
|
-
textus list [--prefix=KEY] --
|
|
389
|
-
textus where KEY
|
|
390
|
-
textus get KEY
|
|
391
|
-
textus put KEY --stdin --
|
|
392
|
-
textus
|
|
393
|
-
textus
|
|
95
|
+
textus #{VERSION} — reference implementation of #{PROTOCOL} (was textus/1)
|
|
96
|
+
|
|
97
|
+
Usage (json output is the default; --format=json accepted for back-compat):
|
|
98
|
+
textus list [--prefix=KEY] [--zone=Z]
|
|
99
|
+
textus where KEY
|
|
100
|
+
textus get KEY
|
|
101
|
+
textus put KEY --stdin [--action=NAME] --as=ROLE
|
|
102
|
+
textus stale [--prefix=KEY] [--zone=Z]
|
|
103
|
+
textus doctor
|
|
104
|
+
textus intro
|
|
105
|
+
textus migrate v2
|
|
106
|
+
|
|
107
|
+
textus key {mv,uid,migrate}
|
|
108
|
+
textus schema {show,init,diff,migrate}
|
|
109
|
+
textus extension {list,run}
|
|
110
|
+
|
|
111
|
+
Deprecated (removed in 0.6): mv, uid, migrate-keys, schema-init,
|
|
112
|
+
schema-diff, schema-migrate, extensions, action.
|
|
394
113
|
HELP
|
|
395
114
|
end
|
|
396
115
|
end
|
|
397
|
-
# rubocop:enable Metrics/ClassLength
|
|
398
116
|
end
|