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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +79 -1
  3. data/README.md +22 -18
  4. data/SPEC.md +49 -35
  5. data/docs/architecture.md +63 -28
  6. data/lib/textus/audit_log.rb +46 -11
  7. data/lib/textus/builder.rb +3 -3
  8. data/lib/textus/{builtin_fetchers.rb → builtin_actions.rb} +16 -11
  9. data/lib/textus/cli/accept.rb +13 -0
  10. data/lib/textus/cli/action.rb +51 -0
  11. data/lib/textus/cli/build.rb +11 -0
  12. data/lib/textus/cli/delete.rb +14 -0
  13. data/lib/textus/cli/deprecated_alias.rb +31 -0
  14. data/lib/textus/cli/deps.rb +10 -0
  15. data/lib/textus/cli/doctor.rb +13 -0
  16. data/lib/textus/cli/extension_group.rb +9 -0
  17. data/lib/textus/cli/extensions.rb +49 -0
  18. data/lib/textus/cli/get.rb +10 -0
  19. data/lib/textus/cli/group.rb +51 -0
  20. data/lib/textus/cli/init.rb +12 -0
  21. data/lib/textus/cli/intro.rb +9 -0
  22. data/lib/textus/cli/key_group.rb +10 -0
  23. data/lib/textus/cli/list.rb +12 -0
  24. data/lib/textus/cli/migrate.rb +41 -0
  25. data/lib/textus/cli/migrate_keys.rb +19 -0
  26. data/lib/textus/cli/mv.rb +20 -0
  27. data/lib/textus/cli/published.rb +9 -0
  28. data/lib/textus/cli/put.rb +48 -0
  29. data/lib/textus/cli/rdeps.rb +10 -0
  30. data/lib/textus/cli/refresh.rb +13 -0
  31. data/lib/textus/cli/schema.rb +10 -0
  32. data/lib/textus/cli/schema_diff.rb +15 -0
  33. data/lib/textus/cli/schema_group.rb +33 -0
  34. data/lib/textus/cli/schema_init.rb +19 -0
  35. data/lib/textus/cli/schema_migrate.rb +19 -0
  36. data/lib/textus/cli/stale.rb +12 -0
  37. data/lib/textus/cli/uid.rb +15 -0
  38. data/lib/textus/cli/verb.rb +62 -0
  39. data/lib/textus/cli/where.rb +10 -0
  40. data/lib/textus/cli.rb +65 -347
  41. data/lib/textus/doctor.rb +103 -32
  42. data/lib/textus/entry/json.rb +6 -4
  43. data/lib/textus/entry/markdown.rb +4 -4
  44. data/lib/textus/entry/text.rb +3 -3
  45. data/lib/textus/entry/yaml.rb +6 -4
  46. data/lib/textus/entry.rb +2 -2
  47. data/lib/textus/errors.rb +2 -2
  48. data/lib/textus/extension_registry.rb +22 -9
  49. data/lib/textus/extensions.rb +6 -2
  50. data/lib/textus/init.rb +6 -5
  51. data/lib/textus/intro.rb +11 -9
  52. data/lib/textus/manifest.rb +11 -215
  53. data/lib/textus/manifest_entry.rb +185 -0
  54. data/lib/textus/migrate_v2.rb +27 -0
  55. data/lib/textus/projection.rb +1 -1
  56. data/lib/textus/proposal.rb +3 -3
  57. data/lib/textus/refresh.rb +21 -20
  58. data/lib/textus/schema_tools.rb +8 -8
  59. data/lib/textus/store/events.rb +31 -0
  60. data/lib/textus/store/mover.rb +118 -0
  61. data/lib/textus/store/staleness.rb +142 -0
  62. data/lib/textus/store/validator.rb +53 -0
  63. data/lib/textus/store.rb +50 -355
  64. data/lib/textus/store_view.rb +11 -2
  65. data/lib/textus/version.rb +2 -2
  66. data/lib/textus.rb +39 -1
  67. 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,12 @@
1
+ module Textus
2
+ class CLI
3
+ class Stale < Verb
4
+ option :prefix, "--prefix=KEY"
5
+ option :zone, "--zone=Z"
6
+
7
+ def call(store)
8
+ emit(store.stale(prefix: prefix, zone: zone))
9
+ end
10
+ end
11
+ end
12
+ 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
@@ -0,0 +1,10 @@
1
+ module Textus
2
+ class CLI
3
+ class Where < Verb
4
+ def call(store)
5
+ key = positional.shift or raise UsageError.new("where requires a key")
6
+ emit(store.where(key))
7
+ end
8
+ end
9
+ end
10
+ 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) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
23
- OptionParser.new do |o|
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 raise UsageError.new("unknown verb: #{verb}")
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 parse_format!(argv)
71
- fmt = "text"
72
- OptionParser.new do |o|
73
- o.on("--format=FMT") { |v| fmt = v }
74
- end.permute!(argv)
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] --format=json
389
- textus where KEY --format=json
390
- textus get KEY --format=json
391
- textus put KEY --stdin --format=json
392
- textus schema KEY --format=json
393
- textus stale [--prefix=KEY] --format=json
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