qb 0.3.25 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/ansible.cfg +10 -1
  4. data/exe/.qb_interop_receive +3 -10
  5. data/exe/qb +8 -2
  6. data/lib/python/qb/__init__.py +6 -0
  7. data/{roles/qb/ruby/rspec/setup/tasks/persistence.yml → lib/python/qb/ansible/__init__.py} +0 -0
  8. data/lib/python/qb/ansible/modules/__init__.py +0 -0
  9. data/lib/python/qb/ansible/modules/docker/__init__.py +0 -0
  10. data/lib/python/qb/ansible/modules/docker/client.py +177 -0
  11. data/lib/python/qb/ansible/modules/docker/image_manager.py +754 -0
  12. data/lib/python/qb/ipc/__init__.py +0 -0
  13. data/lib/python/qb/ipc/stdio/__init__.py +99 -0
  14. data/lib/python/qb/ipc/stdio/logging.py +151 -0
  15. data/lib/qb.rb +3 -3
  16. data/lib/qb/ansible/cmds/playbook.rb +5 -14
  17. data/lib/qb/ansible/env.rb +36 -6
  18. data/lib/qb/ansible/module.rb +396 -152
  19. data/lib/qb/ansible/module/response.rb +195 -0
  20. data/lib/qb/ansible/modules.rb +42 -0
  21. data/lib/qb/ansible/modules/docker/image.rb +273 -0
  22. data/lib/qb/cli.rb +5 -18
  23. data/lib/qb/cli/run.rb +2 -2
  24. data/lib/qb/data.rb +22 -0
  25. data/lib/qb/data/immutable.rb +39 -0
  26. data/lib/qb/docker.rb +2 -0
  27. data/lib/qb/docker/cli.rb +430 -0
  28. data/lib/qb/docker/image.rb +207 -0
  29. data/lib/qb/docker/image/name.rb +309 -0
  30. data/lib/qb/docker/image/tag.rb +113 -0
  31. data/lib/qb/docker/repo.rb +0 -0
  32. data/lib/qb/errors.rb +17 -3
  33. data/lib/qb/execution.rb +83 -0
  34. data/lib/qb/ipc.rb +48 -0
  35. data/lib/qb/ipc/stdio.rb +32 -0
  36. data/lib/qb/ipc/stdio/client.rb +267 -0
  37. data/lib/qb/ipc/stdio/server.rb +229 -0
  38. data/lib/qb/ipc/stdio/server/in_service.rb +18 -0
  39. data/lib/qb/ipc/stdio/server/log_service.rb +168 -0
  40. data/lib/qb/ipc/stdio/server/out_service.rb +20 -0
  41. data/lib/qb/ipc/stdio/server/service.rb +229 -0
  42. data/lib/qb/options.rb +360 -502
  43. data/lib/qb/options/option.rb +293 -115
  44. data/lib/qb/options/option/option_parser_concern.rb +228 -0
  45. data/lib/qb/options/types.rb +73 -0
  46. data/lib/qb/package.rb +0 -1
  47. data/lib/qb/package/version.rb +179 -58
  48. data/lib/qb/package/version/from.rb +192 -51
  49. data/lib/qb/package/version/leveled.rb +1 -1
  50. data/lib/qb/path.rb +3 -2
  51. data/lib/qb/repo/git.rb +9 -85
  52. data/lib/qb/role/default_dir.rb +2 -2
  53. data/lib/qb/role/errors.rb +2 -8
  54. data/lib/qb/util.rb +1 -2
  55. data/lib/qb/util/bundler.rb +73 -43
  56. data/lib/qb/util/decorators.rb +99 -0
  57. data/lib/qb/util/interop.rb +7 -8
  58. data/lib/qb/util/resource.rb +12 -13
  59. data/lib/qb/version.rb +10 -0
  60. data/library/path_facts +5 -10
  61. data/library/qb.module.rb +105 -0
  62. data/library/stream +6 -26
  63. data/load/ansible/module/autorun.rb +25 -0
  64. data/load/ansible/module/script.rb +123 -0
  65. data/load/rebundle.rb +39 -0
  66. data/plugins/filter/dict_filters.py +56 -0
  67. data/plugins/{filter_plugins/path_plugins.py → filter/path_filters.py} +0 -0
  68. data/plugins/{filter_plugins/ruby_interop_plugins.py → filter/ruby_interop_filters.py} +1 -17
  69. data/plugins/{filter_plugins/string_plugins.py → filter/string_filters.py} +1 -20
  70. data/plugins/{filter_plugins/version_plugins.py → filter/version_filters.py} +3 -18
  71. data/plugins/{lookup_plugins/every.py → lookup/every_lookups.py} +0 -0
  72. data/plugins/{lookup_plugins/resolve.py → lookup/resolve_lookups.py} +0 -0
  73. data/plugins/{lookup_plugins/version.py → lookup/version_lookups.py} +0 -16
  74. data/plugins/test/dict_tests.py +36 -0
  75. data/plugins/test/string_tests.py +36 -0
  76. data/qb.gemspec +7 -3
  77. data/roles/nrser.rb/library/set_fact_with_ruby.rb +3 -9
  78. data/roles/nrser.state_mate/library/state +3 -17
  79. data/roles/qb/call/meta/qb.yml +1 -1
  80. data/roles/qb/dev/ref/repo/git/meta/qb.yml +1 -1
  81. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/defaults/main.yml +1 -1
  82. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/meta/main.yml +3 -2
  83. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/meta/qb.yml +12 -7
  84. data/roles/qb/docker/mac/kubernetes/tasks/main.yml +45 -0
  85. data/roles/qb/git/check/clean/meta/qb.yml +1 -1
  86. data/roles/qb/git/ignore/meta/qb +10 -3
  87. data/roles/qb/git/submodule/update/library/git_submodule_update +17 -27
  88. data/roles/qb/github/pages/setup/meta/qb.yml +1 -1
  89. data/roles/qb/labs/atom/apm/meta/qb.yml +1 -1
  90. data/roles/qb/osx/git/change_case/meta/qb.yml +1 -1
  91. data/roles/qb/osx/notif/meta/qb.yml +1 -1
  92. data/roles/qb/pkg/bump/library/bump +4 -16
  93. data/roles/qb/role/qb/defaults/main.yml +2 -0
  94. data/roles/qb/role/qb/meta/qb.yml +10 -5
  95. data/roles/qb/role/qb/templates/qb.yml.j2 +7 -2
  96. data/roles/qb/role/templates/library/module.rb.j2 +12 -23
  97. data/roles/qb/role/templates/meta/main.yml.j2 +14 -1
  98. data/roles/qb/ruby/bundler/meta/qb.yml +1 -1
  99. data/roles/qb/ruby/dependency/meta/qb.yml +1 -1
  100. data/roles/qb/ruby/gem/bin_stubs/meta/qb.yml +1 -1
  101. data/roles/qb/ruby/gem/bin_stubs/templates/console +8 -2
  102. data/roles/qb/ruby/gem/build/meta/qb.yml +1 -1
  103. data/roles/qb/ruby/gem/new/meta/qb.yml +1 -1
  104. data/roles/qb/ruby/nrser/rspex/generate/meta/qb.yml +5 -5
  105. data/roles/qb/ruby/nrser/rspex/issue/meta/qb.yml +1 -1
  106. data/roles/qb/ruby/yard/clean/meta/qb.yml +1 -1
  107. data/roles/qb/ruby/yard/config/library/yard.get_output_dir +5 -15
  108. data/roles/qb/ruby/yard/config/meta/qb.yml +1 -1
  109. data/roles/qb/ruby/yard/setup/meta/qb.yml +1 -1
  110. metadata +71 -22
  111. data/lib/qb/ansible_module.rb +0 -5
  112. data/lib/qb/util/stdio.rb +0 -187
  113. data/roles/qb/ruby/rspec/setup/tasks/main.yml +0 -4
@@ -3,9 +3,8 @@
3
3
  # Requirements
4
4
  # =======================================================================
5
5
 
6
- # stdlib
7
-
8
6
  # deps
7
+ require 'nrser'
9
8
 
10
9
  # package
11
10
  require_relative './cli/help'
@@ -15,23 +14,11 @@ require_relative './cli/setup'
15
14
  require_relative './cli/list'
16
15
 
17
16
 
18
- # Requirements
19
- # =======================================================================
20
-
21
- require 'nrser/refinements'
22
- using NRSER
23
-
24
-
25
- # Declarations
26
- # =======================================================================
27
-
28
- module QB; end
29
-
30
-
31
17
  # Definitions
32
18
  # =======================================================================
33
19
 
34
- module QB::CLI
20
+ module QB
21
+ module CLI
35
22
 
36
23
 
37
24
  # Constants
@@ -55,7 +42,7 @@ module QB::CLI
55
42
  # ============================================================================
56
43
 
57
44
  # Add {.logger} and {#logger} methods
58
- include SemanticLogger::Loggable
45
+ include NRSER::Log::Mixin
59
46
 
60
47
 
61
48
  # Module (Static) Methods
@@ -202,4 +189,4 @@ module QB::CLI
202
189
  end
203
190
 
204
191
 
205
- end # module QB::CLI
192
+ end; end # module QB::CLI
@@ -180,7 +180,7 @@ module QB::CLI
180
180
  }
181
181
 
182
182
  set_options.values.each do |option|
183
- playbook_role[option.var_name] = option.value
183
+ playbook_role[option.var_name] = option.value_data
184
184
  end
185
185
 
186
186
  play =
@@ -212,7 +212,7 @@ module QB::CLI
212
212
  'vars' => {
213
213
  'role' => role.name,
214
214
  'args' => set_options.map { |option|
215
- [option.var_name, option.value]
215
+ [option.var_name, option.value_data]
216
216
  }.to_h,
217
217
  }
218
218
  }
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+
5
+ # Definitions
6
+ # =======================================================================
7
+
8
+ # This class doesn't do *anything*... at least not yet. It serves as a marker
9
+ # for classes that are data (which must be {NRSER::Props}).
10
+ #
11
+ # @example Testing Membership
12
+ # object.is_a? QB::Data
13
+ #
14
+ module QB
15
+ module Data
16
+ end; end # module QB::Data
17
+
18
+
19
+ # Post-Processing
20
+ # ========================================================================
21
+
22
+ require_relative './data/immutable'
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ require 'nrser/props/immutable/hash'
11
+
12
+
13
+ # Project / Package
14
+ # -----------------------------------------------------------------------
15
+
16
+ require_relative '../data'
17
+
18
+
19
+ # Definitions
20
+ # =======================================================================
21
+
22
+ # Abstract base class for immutable data classes. Based off {Hamster::Hash}.
23
+ #
24
+ # Using {Hamster::Hash}
25
+ #
26
+ module QB
27
+ module Data
28
+ class Immutable < Hamster::Hash
29
+
30
+ # Mixins
31
+ # ========================================================================
32
+
33
+ # Mark as a "data" class. Maybe will add some functionality at some point...
34
+ include QB::Data
35
+
36
+ # Infrastructure for a prop'd class based on {Hamster::Hash}
37
+ include NRSER::Props::Immutable::Hash
38
+
39
+ end; end; end # class QB::Data::Immutable
@@ -0,0 +1,2 @@
1
+ require_relative './docker/cli'
2
+ require_relative './docker/image'
@@ -0,0 +1,430 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Stdlib
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Deps
11
+ # -----------------------------------------------------------------------
12
+
13
+ # Project / Package
14
+ # -----------------------------------------------------------------------
15
+
16
+
17
+ # Refinements
18
+ # =======================================================================
19
+
20
+
21
+ # Declarations
22
+ # =======================================================================
23
+
24
+
25
+ # Definitions
26
+ # =======================================================================
27
+
28
+
29
+ # @todo document QB::Docker::CLI class.
30
+ module QB
31
+ module Docker
32
+ module CLI
33
+
34
+ # Mixins
35
+ # ========================================================================
36
+
37
+ include NRSER::Log::Mixin
38
+
39
+ extend ::MethodDecorators
40
+
41
+
42
+ # Classes
43
+ # ==========================================================================
44
+
45
+ class Error < QB::Error
46
+
47
+ # Make an instance from a {Cmds::Result}.
48
+ #
49
+ # @param [Cmds::Result] result
50
+ # Result of command that error'd.
51
+ #
52
+ # @return [self]
53
+ #
54
+ def self.from_result result
55
+ new \
56
+ ( "Command `#{ result.cmd.truncate 40 }` " +
57
+ "failed with exit status #{ result.status }" ),
58
+ status: result.status,
59
+ stderr: result.err,
60
+ stdout: result.out
61
+ end # .from_result
62
+
63
+ end # class Error
64
+
65
+
66
+ class ManifestNotFoundError < Error; end
67
+
68
+
69
+ # Class Methods
70
+ # ========================================================================
71
+
72
+ # @!group Utility Class Methods
73
+ # --------------------------------------------------------------------------
74
+
75
+ # Create a {Cmds} with formatting options defaulted for how (at least most)
76
+ # `docker` subcommands seem to work.
77
+ #
78
+ # @see https://www.rubydoc.info/gems/cmds
79
+ #
80
+ # @param [String] exe:
81
+ # Docker executable to stick in front of `template`.
82
+ #
83
+ # @param (see Cmds.new)
84
+ #
85
+ # @return [Cmds]
86
+ #
87
+ def self.cmd \
88
+ template,
89
+ exe: 'docker',
90
+ array_mode: :repeat,
91
+ dash_opt_names: true,
92
+ hash_join_string: '=',
93
+ long_opt_separator: ' ',
94
+ **options
95
+ Cmds.new \
96
+ "#{ exe.shellescape } #{ template }",
97
+ array_mode: array_mode,
98
+ dash_opt_names: dash_opt_names,
99
+ hash_join_string: hash_join_string,
100
+ long_opt_separator: long_opt_separator,
101
+ **options
102
+ end # .cmd
103
+
104
+
105
+ # Call {.cmd} with a template suitable for most `docker` subcommands.
106
+ #
107
+ # Format is
108
+ #
109
+ # docker NAME [OPTS] [ARGS]
110
+ #
111
+ # @see .cmd
112
+ #
113
+ # @param [Array<#to_s>] *args
114
+ # Positional CLI arguments.
115
+ #
116
+ # @param [Hash] **opts
117
+ # CLI options.
118
+ #
119
+ # @return [Cmds]
120
+ #
121
+ +QB::Util::Decorators::NoPropsInKwds
122
+ def self.sub_cmd name, *args, **opts
123
+ cmd \
124
+ "<%= sub_cmd %> <%= opts %> <%= *args %>",
125
+ args: args,
126
+ kwds: {
127
+ sub_cmd: name,
128
+ opts: opts,
129
+ }
130
+ end # .sub_cmd
131
+
132
+ # @!endgroup Utility Class Methods # ***************************************
133
+
134
+
135
+ # @!group Sub-Command Class Methods
136
+ # --------------------------------------------------------------------------
137
+
138
+ # Build a `docker images` {Cmds} instance.
139
+ #
140
+ # @param [Array<#to_s>] *args
141
+ # Specific image names to get.
142
+ #
143
+ # @param [nil | Symbol | String] format:
144
+ # The `--format` option. Pass `nil` to use default formatting.
145
+ #
146
+ # @param [Hash] **opts
147
+ # Other CLI options.
148
+ #
149
+ # @return [Cmds]
150
+ #
151
+ +QB::Util::Decorators::NoPropsInKwds
152
+ def self.images_cmd *args, format: :raw, **opts
153
+ sub_cmd :images, *args, format: format, **opts
154
+ end # .images
155
+
156
+
157
+ # Get a hash of image data from `docker images`.
158
+ #
159
+ # @note
160
+ # If you just want the raw `docker images` output use:
161
+ #
162
+ # QB::Docker::CLI.images_cmd.out!
163
+ #
164
+ # @param *args (see .images_cmd)
165
+ # @param format: (see .images_cmd)
166
+ # @param **opts (see .images_cmd)
167
+ #
168
+ # @param [Boolean] load:
169
+ # When `true`, will parse `created_at` to a {Time} and combine
170
+ # `repository` and `tag` to a {QB::Docker::Image::Name}.
171
+ #
172
+ # @param [Boolean] only_named:
173
+ # Filter out images that don't have a name or tag.
174
+ #
175
+ # @return [Array<HashWithIndifferentAccess>]
176
+ # Entry keys and values:
177
+ #
178
+ # - `repository: String`
179
+ #
180
+ # Direct from command output, may be `"<none>"`.
181
+ #
182
+ # - `tag: String`
183
+ #
184
+ # Direct from command output, may be `"<none>"`.
185
+ #
186
+ # - `image_id: String`
187
+ #
188
+ # Short (12 character) image SHA256. Unless you pass the `--no-trunc`
189
+ # option, then it's the full `sha256:...` version.
190
+ #
191
+ # - `created_at: String | Time`
192
+ #
193
+ # Time image was created (I think? Could be tag...)
194
+ #
195
+ # - `String` if `load` keyword is `false`.
196
+ # - `Time` if `load` keyword is `true`.
197
+ #
198
+ # - `virtual_size: String`
199
+ #
200
+ # Direct from command output, like `"1.41GB"`.
201
+ #
202
+ # - `name: void | QB::Docker::Image::Name`
203
+ #
204
+ # Combination of `repository` and `tag` parsed into a
205
+ # {QB::Docker::Image::Name}.
206
+ #
207
+ # Only present if `load` is `true` *and*
208
+ #
209
+ #
210
+ +QB::Util::Decorators::NoPropsInKwds
211
+ def self.images *args, load: true, only_named: true, **opts
212
+ hashes = images_cmd( *args, **opts ).
213
+ out!.
214
+ split( "\n\n" ).
215
+ map { |chunk|
216
+ chunk.lines.map { |line|
217
+ key, _, value = line.chomp.partition ': '
218
+
219
+ if key == 'created_at'
220
+ value = Time.parse value
221
+ end
222
+
223
+ [key, value]
224
+ }.
225
+ to_h.
226
+ with_indifferent_access
227
+ }
228
+
229
+ if only_named
230
+ hashes.reject! { |hash|
231
+ hash.values_at( :repository, :tag ).any? { |v| v == '<none>' }
232
+ }
233
+ end
234
+
235
+ if load
236
+ hashes.each { |hash|
237
+ values = hash.values_at :repository, :tag
238
+
239
+ unless values.any? { |v| v == '<none>' }
240
+ hash[:name] = QB::Docker::Image::Name.from_s values.join( ':' )
241
+ end
242
+ }
243
+ end
244
+
245
+ hashes
246
+ end
247
+
248
+
249
+ +QB::Util::Decorators::NoPropsInKwds
250
+ def self.rmi_cmd *args, **opts
251
+ sub_cmd :rmi, *args, **opts
252
+ end
253
+
254
+ singleton_class.send :alias_method, :remove_images_cmd, :rmi_cmd
255
+
256
+
257
+ +QB::Util::Decorators::NoPropsInKwds
258
+ def self.rmi *args, method: :stream!, **opts
259
+ rmi_cmd( *args, **opts ).public_send method
260
+ end
261
+
262
+ singleton_class.send :alias_method, :remove_images, :rmi
263
+
264
+
265
+ +QB::Util::Decorators::NoPropsInKwds
266
+ def self.inspect_image_cmd *args, **opts
267
+ sub_cmd :inspect, *args, **opts
268
+ end
269
+
270
+
271
+ +QB::Util::Decorators::NoPropsInKwds
272
+ def self.inspect_image *names_or_ids, **opts
273
+ inspect_cmd( *names_or_ids, **opts ).out!.thru { |s| JSON.load s }
274
+ end
275
+
276
+
277
+ +QB::Util::Decorators::NoPropsInKwds
278
+ def self.pull_cmd *args, **opts
279
+ sub_cmd :pull, *args, **opts
280
+ end
281
+
282
+
283
+ # Pull an image.
284
+ #
285
+ # @param [String | QB::Docker::Image::Name] name
286
+ #
287
+ +QB::Util::Decorators::NoPropsInKwds
288
+ def self.pull name, **opts
289
+ logger.info "Pulling #{ name }...",
290
+ name: name,
291
+ opts: opts
292
+
293
+ result = pull_cmd( name, **opts ).capture
294
+
295
+ if result.ok?
296
+ logger.info "Successfully pulled #{ name }."
297
+ else
298
+ logger.info "Failed to pull #{ name }",
299
+ stderr: result.err
300
+ end
301
+
302
+ result
303
+ end
304
+
305
+
306
+ +QB::Util::Decorators::NoPropsInKwds
307
+ def self.push_cmd name, **opts
308
+ sub_cmd :push, name, **opts
309
+ end
310
+
311
+
312
+ +QB::Util::Decorators::NoPropsInKwds
313
+ def self.push name, **opts
314
+ logger.info "Pushing `#{ name }`...", name: name, opts: opts
315
+
316
+ result = push_cmd( name, **opts ).capture
317
+ end
318
+
319
+
320
+ +QB::Util::Decorators::NoPropsInKwds
321
+ def self.build_cmd path_or_url, **opts
322
+ sub_cmd :build, path_or_url, **opts
323
+ end
324
+
325
+
326
+ # +QB::Util::Decorators::NoPropsInKwds
327
+ # def self.build path_or_url, **opts
328
+ #
329
+ # end
330
+
331
+
332
+ def self.tag_cmd current_name, new_name_or_tag
333
+ # Load whatever we have
334
+ current_name = QB::Docker::Image::Name.from current_name
335
+
336
+ new_name_or_tag = [
337
+ QB::Docker::Image::Name,
338
+ QB::Docker::Image::Tag,
339
+ ].try_find { |klass| klass.from new_name_or_tag }
340
+
341
+ new_name = if new_name_or_tag.is_a?( QB::Docker::Image::Name )
342
+ if new_name_or_tag.tag
343
+ new_name_or_tag
344
+ else
345
+ new_name_or_tag.merge tag: current_name.tag
346
+ end
347
+ else
348
+ current_name.merge tag: new_name_or_tag
349
+ end
350
+
351
+ sub_cmd :tag, current_name, new_name_or_tag
352
+ end
353
+
354
+
355
+ # @!endgroup Sub-Command Class Methods # ***********************************
356
+
357
+
358
+ # @!group Sugar Class Methods
359
+ # --------------------------------------------------------------------------
360
+ #
361
+ # Making common shit easier.
362
+ #
363
+
364
+ # Get just image names from `docker images`.
365
+ #
366
+ # @param *args (see .images)
367
+ # @return [Array<QB::Docker::Image::Name>]
368
+ #
369
+ +QB::Util::Decorators::NoPropsInKwds
370
+ def self.image_names *args, **opts
371
+ images( *args, load: true, only_named: true, **opts ).
372
+ map &:name.to_retriever
373
+ end # .image_names
374
+
375
+
376
+ # Is there an image with the name?
377
+ #
378
+ # @param [QB::Docker::Image::Name | String] name
379
+ # @return [Boolean]
380
+ #
381
+ +QB::Util::Decorators::NoPropsInKwds
382
+ def self.image_named? name, **opts
383
+ !images( name, load: false, only_named: false, **opts ).empty?
384
+ end
385
+
386
+
387
+ # Boolean version of {.pull}.
388
+ #
389
+ # @param name (see .pull)
390
+ #
391
+ # @return [Boolean]
392
+ # `true` if the pull succeeded.
393
+ #
394
+ +QB::Util::Decorators::NoPropsInKwds
395
+ def self.pull? name, **opts
396
+ pull( name, **opts ).ok?
397
+ end
398
+
399
+
400
+ # Pull an image by name or raise.
401
+ #
402
+ # @param name (see .pull)
403
+ #
404
+ # @todo
405
+ # Support `@DIGEST`
406
+ #
407
+ # @raise [QB::Docker::CLI::ManifestNotFoundError]
408
+ # If the name was not found in the repository.
409
+ #
410
+ # @raise [QB::Docker::CLI::Error]
411
+ # If anything else goes wrong.
412
+ #
413
+ +QB::Util::Decorators::NoPropsInKwds
414
+ def self.pull! name, **opts
415
+ result = pull name, **opts
416
+
417
+ if result.err =~ /manifest.*not\ found/
418
+ raise QB::Docker::CLI::ManifestNotFoundError.new \
419
+ "Failed to pull - manifest for #{ name } not found",
420
+ name: name,
421
+ cmd: result.cmd,
422
+ stderr: result.err
423
+ else
424
+ raise QB::Docker::CLI::Error.from_result( result )
425
+ end
426
+ end
427
+
428
+ # @!endgroup Sugar Class Methods # *****************************************
429
+
430
+ end; end; end # module QB::Docker::CLI