autoproj 2.10.1 → 2.13.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -8
  3. data/.travis.yml +5 -3
  4. data/autoproj.gemspec +7 -6
  5. data/bin/alog +1 -0
  6. data/bin/autoproj +1 -1
  7. data/bin/autoproj_bootstrap +149 -86
  8. data/bin/autoproj_bootstrap.in +9 -7
  9. data/bin/autoproj_install +148 -82
  10. data/bin/autoproj_install.in +8 -3
  11. data/lib/autoproj.rb +3 -0
  12. data/lib/autoproj/aruba_minitest.rb +15 -0
  13. data/lib/autoproj/autobuild_extensions/dsl.rb +61 -27
  14. data/lib/autoproj/base.rb +35 -6
  15. data/lib/autoproj/cli/base.rb +1 -1
  16. data/lib/autoproj/cli/build.rb +9 -3
  17. data/lib/autoproj/cli/cache.rb +79 -7
  18. data/lib/autoproj/cli/doc.rb +4 -18
  19. data/lib/autoproj/cli/inspection_tool.rb +5 -6
  20. data/lib/autoproj/cli/main.rb +41 -18
  21. data/lib/autoproj/cli/main_doc.rb +86 -0
  22. data/lib/autoproj/cli/main_plugin.rb +3 -0
  23. data/lib/autoproj/cli/main_test.rb +15 -0
  24. data/lib/autoproj/cli/show.rb +12 -18
  25. data/lib/autoproj/cli/status.rb +15 -9
  26. data/lib/autoproj/cli/test.rb +13 -84
  27. data/lib/autoproj/cli/update.rb +77 -19
  28. data/lib/autoproj/cli/utility.rb +136 -0
  29. data/lib/autoproj/configuration.rb +28 -4
  30. data/lib/autoproj/default.osdeps +18 -0
  31. data/lib/autoproj/installation_manifest.rb +7 -5
  32. data/lib/autoproj/manifest.rb +15 -21
  33. data/lib/autoproj/ops/build.rb +23 -27
  34. data/lib/autoproj/ops/cache.rb +151 -33
  35. data/lib/autoproj/ops/cached_env.rb +2 -2
  36. data/lib/autoproj/ops/import.rb +146 -80
  37. data/lib/autoproj/ops/install.rb +140 -79
  38. data/lib/autoproj/ops/phase_reporting.rb +49 -0
  39. data/lib/autoproj/ops/snapshot.rb +2 -1
  40. data/lib/autoproj/ops/tools.rb +2 -2
  41. data/lib/autoproj/os_package_installer.rb +19 -11
  42. data/lib/autoproj/package_definition.rb +29 -10
  43. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +49 -28
  44. data/lib/autoproj/package_managers/bundler_manager.rb +257 -87
  45. data/lib/autoproj/package_managers/homebrew_manager.rb +2 -2
  46. data/lib/autoproj/package_managers/shell_script_manager.rb +44 -24
  47. data/lib/autoproj/package_manifest.rb +49 -34
  48. data/lib/autoproj/package_set.rb +48 -29
  49. data/lib/autoproj/repository_managers/apt.rb +0 -1
  50. data/lib/autoproj/test.rb +29 -10
  51. data/lib/autoproj/variable_expansion.rb +3 -1
  52. data/lib/autoproj/vcs_definition.rb +30 -15
  53. data/lib/autoproj/version.rb +1 -1
  54. data/lib/autoproj/workspace.rb +55 -13
  55. metadata +32 -28
@@ -1,24 +1,10 @@
1
- require 'autoproj/cli/inspection_tool'
1
+ require 'autoproj/cli/utility'
2
2
 
3
3
  module Autoproj
4
4
  module CLI
5
- class Doc < InspectionTool
6
- def validate_options(packages, options)
7
- packages, options = super
8
- if options[:no_deps_shortcut]
9
- options[:deps] = false
10
- end
11
- return packages, options
12
- end
13
-
14
- def run(user_selection, deps: true)
15
- initialize_and_load
16
- packages, _ =
17
- finalize_setup(user_selection, recursive: deps)
18
- packages.each do |pkg|
19
- ws.manifest.find_autobuild_package(pkg).disable_phases('import', 'prepare', 'install')
20
- end
21
- Autobuild.apply(packages, "autoproj-doc", ['doc'])
5
+ class Doc < Utility
6
+ def initialize(ws = Workspace.default, name: 'doc')
7
+ super
22
8
  end
23
9
  end
24
10
  end
@@ -8,9 +8,7 @@ class InspectionTool < Base
8
8
  def initialize_and_load(mainline: nil)
9
9
  Autoproj.silent do
10
10
  ws.setup
11
- if mainline == 'mainline' || mainline == 'true'
12
- mainline = true
13
- end
11
+ mainline = true if %w[mainline true].include?(mainline)
14
12
  ws.load_package_sets(mainline: mainline)
15
13
  ws.config.save
16
14
  ws.setup_all_package_directories
@@ -22,7 +20,7 @@ def initialize_and_load(mainline: nil)
22
20
  # @param [Array<String>] packages the list of package names
23
21
  # @param [Symbol] non_imported_packages whether packages that are
24
22
  # not yet imported should be ignored (:ignore) or returned
25
- # (:return).
23
+ # (:return).
26
24
  # @option options recursive (true) whether the package resolution
27
25
  # should return the package(s) and their dependencies
28
26
  #
@@ -32,7 +30,8 @@ def initialize_and_load(mainline: nil)
32
30
  # the arguments were pointing within the configuration area
33
31
  def finalize_setup(packages = [], non_imported_packages: :ignore, recursive: true, auto_exclude: false)
34
32
  Autoproj.silent do
35
- packages, config_selected = normalize_command_line_package_selection(packages)
33
+ packages, config_selected =
34
+ normalize_command_line_package_selection(packages)
36
35
  # Call resolve_user_selection once to auto-add packages, so
37
36
  # that they're available to e.g. overrides.rb
38
37
  resolve_user_selection(packages)
@@ -41,7 +40,7 @@ def finalize_setup(packages = [], non_imported_packages: :ignore, recursive: tru
41
40
  resolve_selection(packages, recursive: recursive, non_imported_packages: non_imported_packages, auto_exclude: auto_exclude)
42
41
  ws.finalize_setup
43
42
  ws.export_installation_manifest
44
- return source_packages, osdep_packages, resolved_selection, config_selected
43
+ [source_packages, osdep_packages, resolved_selection, config_selected]
45
44
  end
46
45
  end
47
46
  end
@@ -1,5 +1,6 @@
1
1
  require 'thor'
2
2
  require 'tty/color'
3
+ require 'autoproj/cli/main_doc'
3
4
  require 'autoproj/cli/main_test'
4
5
  require 'autoproj/cli/main_plugin'
5
6
  require 'autoproj/cli/main_global'
@@ -37,6 +38,10 @@ class Main < Thor
37
38
  stop_on_unknown_option! :exec
38
39
  check_unknown_options! except: :exec
39
40
 
41
+ def self.exit_on_failure?
42
+ true
43
+ end
44
+
40
45
  class << self
41
46
  # @api private
42
47
  #
@@ -129,8 +134,9 @@ def run_autoproj_cli(filename, classname, report_options, *args, tool_failure_mo
129
134
  end
130
135
  cli = CLI.const_get(classname).new
131
136
  begin
132
- run_args = cli.validate_options(args, options.merge(extra_options))
133
- cli.run(*run_args)
137
+ *run_args, kw = cli.validate_options(args, options.merge(extra_options))
138
+ kw = (kw || {}).transform_keys(&:to_sym)
139
+ cli.run(*run_args, **kw)
134
140
  ensure
135
141
  cli.notify_env_sh_updated if cli.respond_to?(:notify_env_sh_updated)
136
142
  end
@@ -183,15 +189,6 @@ def status(*packages)
183
189
  run_autoproj_cli(:status, :Status, Hash[], *packages)
184
190
  end
185
191
 
186
- desc 'doc [PACKAGES]', 'generate API documentation for packages that support it'
187
- option :deps, type: :boolean, default: true,
188
- desc: 'control whether documentation should be generated only for the packages given on the command line, or also for their dependencies. -n is a shortcut for --no-deps'
189
- option :no_deps_shortcut, hide: true, aliases: '-n', type: :boolean,
190
- desc: 'provide -n for --no-deps'
191
- def doc(*packages)
192
- run_autoproj_cli(:doc, :Doc, Hash[], *packages)
193
- end
194
-
195
192
  desc 'update [PACKAGES]', 'update packages'
196
193
  option :aup, default: false, hide: true, type: :boolean,
197
194
  desc: 'behave like aup'
@@ -232,6 +229,8 @@ def doc(*packages)
232
229
  desc: "compare to the given baseline. if 'true', the comparison will ignore any override, otherwise it will take into account overrides only up to the given package set"
233
230
  option :auto_exclude, type: :boolean,
234
231
  desc: 'if true, packages that fail to import will be excluded from the build'
232
+ option :ask, type: :boolean, default: false,
233
+ desc: 'ask whether each package should or should not be updated'
235
234
  def update(*packages)
236
235
  report_options = Hash[silent: false, on_package_failures: default_report_on_package_failures]
237
236
  if options[:auto_exclude]
@@ -304,13 +303,34 @@ def build(*packages)
304
303
  end
305
304
  end
306
305
 
307
- desc 'cache CACHE_DIR', 'create or update a cache directory that can be given to AUTOBUILD_CACHE_DIR'
308
- option :keep_going, aliases: :k,
309
- desc: 'do not stop on errors'
310
- option :checkout_only, aliases: :c, type: :boolean, default: false,
311
- desc: "only checkout packages, do not update already-cached ones"
312
- option :all, type: :boolean, default: true,
313
- desc: "cache all defined packages (the default) or only the selected ones"
306
+ desc 'cache CACHE_DIR', 'create or update a cache directory that '\
307
+ 'can be given to AUTOBUILD_CACHE_DIR'
308
+ option :keep_going,
309
+ aliases: :k,
310
+ desc: 'do not stop on errors'
311
+ option :checkout_only,
312
+ aliases: :c, type: :boolean, default: false,
313
+ desc: 'only checkout packages, do not update already-cached ones'
314
+ option :all,
315
+ type: :boolean, default: true,
316
+ desc: 'cache all defined packages (the default), '\
317
+ ' or only the selected ones'
318
+ option :packages,
319
+ type: :boolean, default: true,
320
+ desc: 'update the package cache'
321
+ option :gems,
322
+ type: :boolean, default: false,
323
+ desc: 'update the gems cache'
324
+ option :gems_compile_force,
325
+ type: :boolean, default: false,
326
+ desc: 'with --gems-compile, recompile existing gems as well'
327
+ option :gems_compile,
328
+ type: :array,
329
+ desc: 'pre-compile the following gems. This requires gem-compiler '\
330
+ 'to be available in the workspace. Use GEM_NAME+ARTIFACT'\
331
+ '[+ARTIFACT] to add files or directories to the precompiled '\
332
+ 'gems beyond what gem-compiler auto-adds (which is mostly '\
333
+ 'dynamic libraries)'
314
334
  def cache(*args)
315
335
  run_autoproj_cli(:cache, :Cache, Hash[], *args)
316
336
  end
@@ -360,6 +380,9 @@ def reconfigure
360
380
  desc 'test', 'interface for running tests'
361
381
  subcommand 'test', MainTest
362
382
 
383
+ desc 'doc', 'interface for generating documentation'
384
+ subcommand 'doc', MainDoc
385
+
363
386
  desc 'show [PACKAGES]', 'show informations about package(s)'
364
387
  option :mainline, type: :string,
365
388
  desc: "compare to the given baseline. if 'true', the comparison will ignore any override, otherwise it will take into account overrides only up to the given package set"
@@ -0,0 +1,86 @@
1
+ module Autoproj
2
+ module CLI
3
+ class MainDoc < Thor
4
+ namespace 'doc'
5
+
6
+ default_command 'exec'
7
+
8
+ no_commands do
9
+ def report(report_options = Hash.new)
10
+ options = self.options.merge(parent_options)
11
+ extra_options = Hash.new
12
+ if Autobuild::Subprocess.transparent_mode = options[:tool]
13
+ Autobuild.silent = true
14
+ Autobuild.color = false
15
+ report_options[:silent] = true
16
+ report_options[:on_package_failures] = :exit_silent
17
+ extra_options[:silent] = true
18
+ end
19
+ Autoproj.report(**Hash[debug: options[:debug]].merge(report_options)) do
20
+ yield(extra_options)
21
+ end
22
+ end
23
+ end
24
+
25
+ desc 'enable [PACKAGES]', 'enable docs for the given packages (or for all packages if none are given)'
26
+ option :deps, type: :boolean, default: false,
27
+ desc: 'controls whether the dependencies of the packages given on the command line should be enabled as well (the default is not)'
28
+ def enable(*packages)
29
+ require 'autoproj/cli/doc'
30
+ report(silent: true) do
31
+ cli = Doc.new
32
+ args = cli.validate_options(packages, options)
33
+ cli.enable(*args)
34
+ end
35
+ end
36
+
37
+ desc 'disable [PACKAGES]', 'disable docs for the given packages (or for all packages if none are given)'
38
+ option :deps, type: :boolean, default: false,
39
+ desc: 'controls whether the dependencies of the packages given on the command line should be disabled as well (the default is not)'
40
+ def disable(*packages)
41
+ require 'autoproj/cli/doc'
42
+ report(silent: true) do
43
+ cli = Doc.new
44
+ args = cli.validate_options(packages, options)
45
+ cli.disable(*args)
46
+ end
47
+ end
48
+
49
+ desc 'list [PACKAGES]', 'show doc enable/disable status for the given packages (or all packages if none are given)'
50
+ option :deps, type: :boolean, default: true,
51
+ desc: 'controls whether the dependencies of the packages given on the command line should be disabled as well (the default is not)'
52
+ def list(*packages)
53
+ require 'autoproj/cli/doc'
54
+ report(silent: true) do
55
+ cli = Doc.new
56
+ args = cli.validate_options(packages, options)
57
+ cli.list(*args)
58
+ end
59
+ end
60
+
61
+ desc 'exec [PACKAGES]', 'generate documentation for the given packages, or all if no packages are given on the command line'
62
+ option :deps, type: :boolean, default: false,
63
+ desc: 'controls whether to generate documentation of the dependencies of the packages given on the command line (the default is not)'
64
+ option :no_deps_shortcut, hide: true, aliases: '-n', type: :boolean,
65
+ desc: 'provide -n for --no-deps'
66
+ option :parallel, aliases: :p, type: :numeric,
67
+ desc: 'maximum number of parallel jobs'
68
+ option :tool, type: :boolean, default: false,
69
+ desc: "run in tool mode, which do not redirect the subcommand's outputs"
70
+ option :color, type: :boolean, default: TTY::Color.color?,
71
+ desc: 'enables or disables colored display (enabled by default if the terminal supports it)'
72
+ option :progress, type: :boolean, default: TTY::Color.color?,
73
+ desc: 'enables or disables progress display (enabled by default if the terminal supports it)'
74
+ def exec(*packages)
75
+ require 'autoproj/cli/doc'
76
+ options = self.options.merge(parent_options)
77
+ report do |extra_options|
78
+ cli = Doc.new
79
+ options.delete(:tool)
80
+ args = cli.validate_options(packages, options.merge(extra_options))
81
+ cli.run(*args)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -30,6 +30,8 @@ def write_plugin_list(plugins)
30
30
  type: 'string', default: '>= 0'
31
31
  option :git, desc: 'checkout a git repository instead of downloading the gem',
32
32
  type: 'string'
33
+ option :branch, desc: 'choose the branch that should be checked out with --git',
34
+ type: 'string', default: 'master'
33
35
  option :path, desc: 'use the plugin that is already present on this path',
34
36
  type: 'string'
35
37
  def install(name)
@@ -40,6 +42,7 @@ def install(name)
40
42
  raise CLIInvalidArguments, "you can provide only one of --git or --path"
41
43
  elsif options[:git]
42
44
  gem_options[:git] = options[:git]
45
+ gem_options[:branch] = options[:branch]
43
46
  elsif options[:path]
44
47
  gem_options[:path] = options[:path]
45
48
  end
@@ -22,6 +22,21 @@ def report(report_options = Hash.new)
22
22
  end
23
23
  end
24
24
 
25
+ desc 'default [on|off]', 'set whether tests are enabled or disabled by default, without touching existing settings'
26
+ def default(on_or_off)
27
+ require 'autoproj/cli/test'
28
+ report(silent: true) do
29
+ cli = Test.new
30
+ args = cli.validate_options([], options)
31
+ enabled = case on_or_off
32
+ when 'on' then true
33
+ when 'off' then false
34
+ else raise ArgumentError, "expected 'on' or 'off'"
35
+ end
36
+ cli.default(enabled)
37
+ end
38
+ end
39
+
25
40
  desc 'enable [PACKAGES]', 'enable tests for the given packages (or for all packages if none are given)'
26
41
  option :deps, type: :boolean, default: false,
27
42
  desc: 'controls whether the dependencies of the packages given on the command line should be enabled as well (the default is not)'
@@ -17,7 +17,8 @@ def run(user_selection, short: false, recursive: false, mainline: false, env: fa
17
17
  source_packages, osdep_packages, * =
18
18
  finalize_setup(user_selection, recursive: recursive, non_imported_packages: :return)
19
19
  else
20
- source_packages, osdep_packages = Array.new, Array.new
20
+ source_packages = []
21
+ osdep_packages = []
21
22
  end
22
23
 
23
24
  all_matching_osdeps = osdep_packages.map { |pkg| [pkg, true] }
@@ -232,7 +233,7 @@ def display_common_information(pkg_name, default_packages, revdeps)
232
233
  end
233
234
  end
234
235
 
235
- if !selections.empty?
236
+ unless selections.empty?
236
237
  puts " selected by way of"
237
238
  selections.each do |root_pkg|
238
239
  paths = find_selection_path(root_pkg, pkg_name)
@@ -248,9 +249,7 @@ def display_common_information(pkg_name, default_packages, revdeps)
248
249
  end
249
250
 
250
251
  def find_selection_path(from, to)
251
- if from == to
252
- return [[from]]
253
- end
252
+ return [[from]] if from == to
254
253
 
255
254
  all_paths = Array.new
256
255
  ws.manifest.resolve_package_name(from).each do |pkg_type, pkg_name|
@@ -262,19 +261,17 @@ def find_selection_path(from, to)
262
261
 
263
262
  pkg = ws.manifest.find_autobuild_package(pkg_name)
264
263
  pkg.dependencies.each do |dep_pkg_name|
265
- if result = find_selection_path(dep_pkg_name, to)
264
+ if (result = find_selection_path(dep_pkg_name, to))
266
265
  all_paths.concat(result.map { |p| path + p })
267
266
  end
268
267
  end
269
- if pkg.os_packages.include?(to)
270
- all_paths << (path + [to])
271
- end
268
+ all_paths << (path + [to]) if pkg.os_packages.include?(to)
272
269
  end
273
270
 
274
271
  # Now filter common trailing subpaths
275
272
  all_paths = all_paths.sort_by(&:size)
276
273
  filtered_paths = Array.new
277
- while !all_paths.empty?
274
+ until all_paths.empty?
278
275
  path = all_paths.shift
279
276
  filtered_paths << path
280
277
  size = path.size
@@ -290,19 +287,15 @@ def vcs_to_array(vcs)
290
287
  options = vcs.dup
291
288
  type = options.delete('type')
292
289
  url = options.delete('url')
293
- else
290
+ else
294
291
  options = vcs.options
295
292
  type = vcs.type
296
293
  url = vcs.url
297
294
  end
298
295
 
299
296
  fields = []
300
- if type
301
- fields << ['type', type]
302
- end
303
- if url
304
- fields << ['url', url]
305
- end
297
+ fields << ['type', type] if type
298
+ fields << ['url', url] if url
306
299
  fields = fields.concat(options.to_a.sort_by { |k, _| k.to_s })
307
300
  fields.map do |key, value|
308
301
  if value.respond_to?(:to_str) && File.file?(value) && value =~ /^\//
@@ -315,9 +308,10 @@ def vcs_to_array(vcs)
315
308
  def compute_all_revdeps(pkg_revdeps, revdeps)
316
309
  pkg_revdeps = pkg_revdeps.dup
317
310
  all_revdeps = Array.new
318
- while !pkg_revdeps.empty?
311
+ until pkg_revdeps.empty?
319
312
  parent_name = pkg_revdeps.shift
320
313
  next if all_revdeps.include?(parent_name)
314
+
321
315
  all_revdeps << parent_name
322
316
  pkg_revdeps.concat(revdeps[parent_name].to_a)
323
317
  end
@@ -73,7 +73,7 @@ def snapshot_overrides_vcs?(importer, vcs, snapshot)
73
73
  end
74
74
  end
75
75
 
76
- def report_exception(package_status, msg, e)
76
+ def self.report_exception(package_status, msg, e)
77
77
  package_status.msg << Autoproj.color(" #{msg} (#{e})", :red)
78
78
  if Autobuild.debug
79
79
  package_status.msg.concat(e.backtrace.map do |line|
@@ -82,8 +82,8 @@ def report_exception(package_status, msg, e)
82
82
  end
83
83
  end
84
84
 
85
- PackageStatus = Struct.new :msg, :sync, :uncommitted, :local, :remote
86
- def status_of_package(package_description, only_local: false, snapshot: false)
85
+ PackageStatus = Struct.new :msg, :sync, :unexpected, :uncommitted, :local, :remote
86
+ def self.status_of_package(package_description, only_local: false, snapshot: false)
87
87
  pkg = package_description.autobuild
88
88
  importer = pkg.importer
89
89
  package_status = PackageStatus.new(Array.new, false, false, false, false)
@@ -96,7 +96,7 @@ def status_of_package(package_description, only_local: false, snapshot: false)
96
96
  else
97
97
  begin status = importer.status(pkg, only_local: only_local)
98
98
  rescue StandardError => e
99
- report_exception(package_status, "failed to fetch status information", e)
99
+ self.report_exception(package_status, "failed to fetch status information", e)
100
100
  return package_status
101
101
  end
102
102
 
@@ -108,7 +108,7 @@ def status_of_package(package_description, only_local: false, snapshot: false)
108
108
  rescue Autobuild::PackageException
109
109
  Hash.new
110
110
  rescue StandardError => e
111
- report_exception(package_status, "failed to fetch snapshotting information", e)
111
+ self.report_exception(package_status, "failed to fetch snapshotting information", e)
112
112
  return package_status
113
113
  end
114
114
  if snapshot_overrides_vcs?(importer, package_description.vcs, snapshot_version)
@@ -122,6 +122,7 @@ def status_of_package(package_description, only_local: false, snapshot: false)
122
122
  end
123
123
 
124
124
  status.unexpected_working_copy_state.each do |msg|
125
+ package_status.unexpected = true
125
126
  package_status.msg << Autoproj.color(" #{msg}", :red, :bold)
126
127
  end
127
128
 
@@ -173,13 +174,15 @@ def each_package_status(packages, parallel: ws.config.parallel_import_level, sna
173
174
  end
174
175
  noninteractive = noninteractive.map do |pkg|
175
176
  future = Concurrent::Future.execute(executor: executor) do
176
- status_of_package(pkg, snapshot: snapshot, only_local: only_local)
177
+ Status.status_of_package(
178
+ pkg, snapshot: snapshot, only_local: only_local
179
+ )
177
180
  end
178
181
  [pkg, future]
179
182
  end
180
183
 
181
184
  (noninteractive + interactive).each do |pkg, future|
182
- if future
185
+ if future
183
186
  if progress
184
187
  wait_timeout = 1
185
188
  while true
@@ -196,7 +199,10 @@ def each_package_status(packages, parallel: ws.config.parallel_import_level, sna
196
199
  if !(status = future.value)
197
200
  raise future.reason
198
201
  end
199
- else status = status_of_package(pkg, snapshot: snapshot, only_local: only_local)
202
+ else
203
+ status = Status.status_of_package(
204
+ pkg, snapshot: snapshot, only_local: only_local
205
+ )
200
206
  end
201
207
 
202
208
  result.uncommitted ||= status.uncommitted
@@ -263,7 +269,7 @@ def display_status(packages, parallel: ws.config.parallel_import_level, snapshot
263
269
  sync_packages = ""
264
270
  end
265
271
 
266
- STDERR.print
272
+ STDERR.print
267
273
 
268
274
  if status.msg.size == 1
269
275
  Autoproj.message "#{pkg_name}: #{status.msg.first}"