autoproj 2.0.0.b5 → 2.0.0.b6

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.
@@ -11,15 +11,14 @@ module Autoproj
11
11
  raise ConfigError, "you cannot run autoproj switch-config from autoproj's configuration directory or one of its subdirectories"
12
12
  end
13
13
 
14
+ ws.load_config
15
+
14
16
  # We must switch to the root dir first, as it is required by the
15
17
  # configuration switch code. This is acceptable as long as we
16
18
  # quit just after the switch
17
19
  switcher = Ops::MainConfigSwitcher.new(ws)
18
20
  if switcher.switch_config(*args)
19
- manifest = Manifest.load(File.join(ws.config_dir, 'manifest'))
20
- update = Ops::Configuration.new(ws, ws.loader)
21
- update.update_configuration
22
- ws.config.save
21
+ CLI::Main.start(['update', '--config'])
23
22
  end
24
23
  end
25
24
  end
@@ -21,10 +21,20 @@ module Autoproj
21
21
  packages = ['.']
22
22
  end
23
23
 
24
+ if options[:force_reset]
25
+ options[:reset] = :force
26
+ end
27
+
24
28
  if options[:autoproj].nil?
25
29
  options[:autoproj] = packages.empty?
26
30
  end
27
31
 
32
+ if mainline = options[:mainline]
33
+ if mainline == 'mainline' || mainline == 'true'
34
+ options[:mainline] = true
35
+ end
36
+ end
37
+
28
38
  return packages, options
29
39
  end
30
40
 
@@ -32,15 +42,12 @@ module Autoproj
32
42
  selected_packages, config_selected =
33
43
  normalize_command_line_package_selection(selected_packages)
34
44
 
35
- if options[:apply_post_install].nil?
36
- options[:apply_post_install] = true
37
- end
38
-
39
45
  if options[:config].nil?
40
46
  options[:config] = selected_packages.empty? || config_selected
41
47
  end
42
48
 
43
49
  ws.setup
50
+ parallel = options[:parallel] || ws.config.parallel_import_level
44
51
 
45
52
  # Do that AFTER we have properly setup ws.osdeps as to avoid
46
53
  # unnecessarily redetecting the operating system
@@ -56,11 +63,14 @@ module Autoproj
56
63
  end
57
64
 
58
65
  ws.load_package_sets(
66
+ mainline: options[:mainline],
59
67
  only_local: options[:local],
60
68
  checkout_only: !options[:config] || options[:checkout_only],
61
- ignore_errors: options[:keep_going])
62
- if selected_packages.empty? && config_selected
63
- return
69
+ reset: options[:reset],
70
+ ignore_errors: options[:keep_going],
71
+ retry_count: options[:retry_count])
72
+ if selected_packages.empty? && (config_selected || options[:config]) && !options[:all]
73
+ return [], [], true
64
74
  end
65
75
 
66
76
  ws.setup_all_package_directories
@@ -112,12 +122,11 @@ module Autoproj
112
122
  only_local: options[:local],
113
123
  reset: options[:reset],
114
124
  recursive: options[:deps],
115
- ignore_errors: options[:keep_going])
125
+ ignore_errors: options[:keep_going],
126
+ parallel: parallel,
127
+ retry_count: options[:retry_count])
116
128
 
117
129
  ws.finalize_setup
118
- if options[:apply_post_install]
119
- ws.apply_post_install
120
- end
121
130
  ws.export_installation_manifest
122
131
 
123
132
  if options[:osdeps] && !osdep_packages.empty?
@@ -14,11 +14,11 @@ module Autoproj
14
14
 
15
15
  def validate_options(packages, options = Hash.new)
16
16
  packages, options = super
17
- if options[:save].nil?
17
+ if !options[:save].nil?
18
18
  options[:save] = case options[:save]
19
19
  when '.'
20
20
  nil
21
- when true
21
+ when 'save'
22
22
  default_versions_file
23
23
  else
24
24
  options[:save].to_str
@@ -29,21 +29,23 @@ module Autoproj
29
29
 
30
30
  def run(user_selection, options)
31
31
  initialize_and_load
32
- packages, *, config_selected =
32
+ user_selection, config_selected =
33
+ normalize_command_line_package_selection(user_selection)
34
+ packages, * =
33
35
  finalize_setup(user_selection,
34
36
  ignore_non_imported_packages: true)
35
37
 
36
- if (config_selected || user_selection.empty?) && (options[:package_sets] != false)
37
- options[:package_sets] = true
38
- end
39
-
40
- ops = Ops::Snapshot.new(ws.manifest, keep_going: options[:keep_going])
38
+
39
+ ops = Ops::Snapshot.new(ws.manifest, ignore_errors: options[:keep_going])
41
40
 
42
41
  versions = Array.new
43
- if options[:package_sets]
42
+ if config_selected || (options[:config] != false) || user_selection.empty?
44
43
  versions += ops.snapshot_package_sets
45
44
  end
46
- versions += ops.snapshot_packages(packages)
45
+ if (!config_selected && !options[:config]) || !user_selection.empty?
46
+ versions += ops.snapshot_packages(packages)
47
+ end
48
+
47
49
  if output_file = options[:save]
48
50
  ops.save_versions(versions, output_file, replace: options[:replace])
49
51
  else
@@ -189,6 +189,14 @@ module Autoproj
189
189
  set('import_log_enabled', !!value)
190
190
  end
191
191
 
192
+ def parallel_import_level
193
+ get('parallel_import_level', 10)
194
+ end
195
+
196
+ def parallel_import_level=(level)
197
+ set('parallel_import_level', level)
198
+ end
199
+
192
200
  def ruby_executable
193
201
  if path = get('ruby_executable', nil)
194
202
  path
@@ -1,105 +1,3 @@
1
- module Autoproj
2
- # Adds the relevant options to handle a gitorious server
3
- # What this does is ask the user how he would like to access the gitorious
4
- # server. Then, it sets
5
- #
6
- # #{name}_ROOT to be the base URL for pulling
7
- # #{name}_PUSH_ROOT to be the corresponding ssh-based URL for pushing
8
- #
9
- # For instance, with
10
- #
11
- # handle_gitorious_server "GITORIOUS", "gitorious.org"
12
- #
13
- # the URLs for the rtt repository in the orocos-toolchain gitorious project would
14
- # be defined in the source.yml files as
15
- #
16
- # ${GITORIOUS_ROOT}/orocos-toolchain/rtt.git
17
- # ${GITORIOUS_PUSH_ROOT}/orocos-toolchain/rtt.git
18
- #
19
- # Since it seems that the http method for gitorious servers is more stable, a
20
- # fallback importer is set up that falls back to using http for pulling as soon
21
- # as import failed
22
- def self.gitorious_server_configuration(name, base_url, options = Hash.new)
23
- options = Kernel.validate_options options,
24
- :git_url => "git://#{base_url}",
25
- :http_url => "https://git.#{base_url}",
26
- :ssh_url => "git@#{base_url}:",
27
- :fallback_to_http => true,
28
- :default => 'http,ssh',
29
- :disabled_methods => [],
30
- config: Autoproj.config
31
-
32
- config = options.delete(:config)
33
- disabled_methods = Array(options[:disabled_methods])
34
-
35
- access_methods = Hash[
36
- 'git' => 'git,ssh',
37
- 'ssh' => 'ssh,ssh',
38
- 'http' => 'http,http']
39
-
40
- gitorious_long_doc = [
41
- "How should I interact with #{base_url} (#{(access_methods.keys - disabled_methods).sort.join(", ")})",
42
- "If you give two values, comma-separated, the first one will be",
43
- "used for pulling and the second one for pushing"]
44
-
45
- validator = lambda do |value|
46
- if value =~ /,/
47
- value.split(',').each do |method|
48
- if !access_methods.has_key?(method)
49
- raise Autoproj::InputError, "#{method} is not a known access method"
50
- elsif disabled_methods.include?(method)
51
- raise Autoproj::InputError, "#{method} is disabled on #{base_url}"
52
- end
53
- end
54
- elsif !access_methods.has_key?(value)
55
- raise Autoproj::InputError, "#{value} is not a known access method"
56
- elsif disabled_methods.include?(value)
57
- raise Autoproj::InputError, "#{value} is disabled on #{base_url}"
58
- end
59
-
60
- value
61
- end
62
-
63
- config.declare name, 'string',
64
- :default => options[:default],
65
- :doc => gitorious_long_doc, &validator
66
-
67
- access_mode = config.get(name)
68
- begin
69
- validator[access_mode]
70
- rescue Autoproj::InputError => e
71
- Autoproj.warn e.message
72
- config.reset(name)
73
- access_mode = config.get(name)
74
- end
75
- access_mode = access_methods[access_mode] || access_mode
76
- pull, push = access_mode.split(',')
77
- [[pull, "_ROOT"], [push, "_PUSH_ROOT"]].each do |method, var_suffix|
78
- url = if method == "git" then options[:git_url]
79
- elsif method == "http" then options[:http_url]
80
- elsif method == "ssh" then options[:ssh_url]
81
- end
82
- config.set("#{name}#{var_suffix}", url)
83
- end
84
-
85
- Autoproj.add_source_handler name.downcase do |url, vcs_options|
86
- if url !~ /\.git$/
87
- url += ".git"
88
- end
89
- if url !~ /^\//
90
- url = "/#{url}"
91
- end
92
- pull_base_url = config.get("#{name}_ROOT")
93
- push_base_url = config.get("#{name}_PUSH_ROOT")
94
- Hash[type: 'git',
95
- url: "#{pull_base_url}#{url}",
96
- push_to: "#{push_base_url}#{url}",
97
- retry_count: 10,
98
- repository_id: "#{name.downcase}:#{url}"].merge(vcs_options)
99
- end
100
- end
101
- end
102
-
103
- Autoproj.gitorious_server_configuration('GITORIOUS', 'gitorious.org', :default => 'http,ssh', :disabled_methods => 'git')
104
- Autoproj.gitorious_server_configuration('GITHUB', 'github.com', :http_url => 'https://github.com', :default => 'http,ssh')
105
-
1
+ Autoproj.warn "gitorious_server_configuration is deprecated, use require 'git_server_configuration' and Autoproj.git_server_configuration instead"
2
+ Autoproj.warn "note that the method call interface has not changed, you just have to change the name(s)"
3
+ require 'autoproj/git_server_configuration'
@@ -66,7 +66,7 @@ module Autoproj
66
66
  end
67
67
 
68
68
  def each(&block)
69
- packages.each(&block)
69
+ packages.each_value(&block)
70
70
  end
71
71
 
72
72
  def [](name)
@@ -56,7 +56,9 @@ module Autoproj
56
56
  options = Kernel.validate_options options,
57
57
  only_local: false,
58
58
  checkout_only: !Autobuild.do_update,
59
- ignore_errors: false
59
+ ignore_errors: false,
60
+ reset: false,
61
+ retry_count: nil
60
62
 
61
63
  fake_package = Tools.create_autobuild_package(vcs, name, into)
62
64
  if update_from
@@ -79,6 +81,9 @@ module Autoproj
79
81
  fake_package.update = false
80
82
  end
81
83
  end
84
+ if retry_count = options.delete(:retry_count)
85
+ fake_package.importer.retry_count = retry_count
86
+ end
82
87
  fake_package.import(options)
83
88
 
84
89
  rescue Autobuild::ConfigException => e
@@ -96,7 +101,9 @@ module Autoproj
96
101
  options = validate_options options,
97
102
  only_local: false,
98
103
  checkout_only: !Autobuild.do_update,
99
- ignore_errors: false
104
+ ignore_errors: false,
105
+ reset: false,
106
+ retry_count: nil
100
107
 
101
108
  update_configuration_repository(
102
109
  ws.manifest.vcs,
@@ -118,7 +125,9 @@ module Autoproj
118
125
  options = validate_options options,
119
126
  only_local: false,
120
127
  checkout_only: !Autobuild.do_update,
121
- ignore_errors: false
128
+ ignore_errors: false,
129
+ reset: false,
130
+ retry_count: nil
122
131
 
123
132
  name = PackageSet.name_of(ws.manifest, vcs)
124
133
  raw_local_dir = PackageSet.raw_local_dir_of(vcs)
@@ -208,7 +217,9 @@ module Autoproj
208
217
  options = validate_options options,
209
218
  only_local: false,
210
219
  checkout_only: !Autobuild.do_update,
211
- ignore_errors: false
220
+ ignore_errors: false,
221
+ reset: false,
222
+ retry_count: nil
212
223
 
213
224
  package_sets = [root_pkg_set]
214
225
  by_repository_id = Hash.new
@@ -339,7 +350,9 @@ module Autoproj
339
350
  options = validate_options options,
340
351
  only_local: false,
341
352
  checkout_only: true,
342
- ignore_errors: false
353
+ ignore_errors: false,
354
+ reset: false,
355
+ retry_count: nil
343
356
  update_configuration(options)
344
357
  end
345
358
 
@@ -350,7 +363,9 @@ module Autoproj
350
363
  options = validate_options options,
351
364
  only_local: false,
352
365
  checkout_only: !Autobuild.do_update,
353
- ignore_errors: false
366
+ ignore_errors: false,
367
+ reset: false,
368
+ retry_count: nil
354
369
 
355
370
  # Load the installation's manifest a first time, to check if we should
356
371
  # update it ... We assume that the OS dependencies for this VCS is already
@@ -63,12 +63,65 @@ module Autoproj
63
63
  new_packages
64
64
  end
65
65
 
66
+ def pre_package_import(selection, manifest, pkg, reverse_dependencies)
67
+ # Try to auto-exclude the package early. If the autobuild file
68
+ # contained some information that allows us to exclude it now,
69
+ # then let's just do it
70
+ import_next_step(pkg, reverse_dependencies)
71
+ if manifest.excluded?(pkg.name)
72
+ selection.filter_excluded_and_ignored_packages(manifest)
73
+ false
74
+ elsif manifest.ignored?(pkg.name)
75
+ false
76
+ elsif !pkg.importer && !File.directory?(pkg.srcdir)
77
+ raise ConfigError.new, "#{pkg.name} has no VCS, but is not checked out in #{pkg.srcdir}"
78
+ elsif pkg.importer
79
+ true
80
+ end
81
+ end
82
+
83
+ def post_package_import(selection, manifest, pkg, reverse_dependencies)
84
+ Rake::Task["#{pkg.name}-import"].instance_variable_set(:@already_invoked, true)
85
+ manifest.load_package_manifest(pkg.name)
86
+
87
+ # The package setup mechanisms might have added an exclusion
88
+ # on this package. Handle this.
89
+ if manifest.excluded?(pkg.name)
90
+ mark_exclusion_along_revdeps(pkg.name, reverse_dependencies)
91
+ # Run a filter now, to have errors as early as possible
92
+ selection.filter_excluded_and_ignored_packages(manifest)
93
+ # Delete this package from the current_packages set
94
+ false
95
+ elsif manifest.ignored?(pkg.name)
96
+ false
97
+ else
98
+ Autoproj.each_post_import_block(pkg) do |block|
99
+ block.call(pkg)
100
+ end
101
+ import_next_step(pkg, reverse_dependencies)
102
+ end
103
+ end
104
+
105
+ class ImportFailed < RuntimeError; end
106
+
66
107
  # Import all packages from the given selection, and their
67
108
  # dependencies
68
109
  def import_selected_packages(selection, updated_packages, options = Hash.new)
110
+ all_processed_packages = Set.new
111
+
112
+ parallel_options, options = Kernel.filter_options options,
113
+ parallel: ws.config.parallel_import_level
114
+
115
+ # This is used in the ensure block, initialize as early as
116
+ # possible
117
+ executor = Concurrent::FixedThreadPool.new(parallel_options[:parallel], max_length: 0)
118
+
69
119
  options, import_options = Kernel.filter_options options,
70
- recursive: true
120
+ recursive: true,
121
+ retry_count: nil
71
122
 
123
+ ignore_errors = options[:ignore_errors]
124
+ retry_count = options[:retry_count]
72
125
  manifest = ws.manifest
73
126
 
74
127
  selected_packages = selection.each_source_package_name.map do |pkg_name|
@@ -83,72 +136,107 @@ module Autoproj
83
136
  # package exclusion (and that does not affect optional dependencies)
84
137
  reverse_dependencies = Hash.new { |h, k| h[k] = Set.new }
85
138
 
139
+ completion_queue = Queue.new
140
+ pending_packages = Set.new
86
141
  # The set of all packages that are currently selected by +selection+
87
142
  all_processed_packages = Set.new
143
+ interactive_imports = Array.new
88
144
  package_queue = selected_packages.to_a.sort_by(&:name)
89
- while !package_queue.empty?
90
- pkg = package_queue.shift
91
- # Remove packages that have already been processed
92
- next if all_processed_packages.include?(pkg.name)
93
- all_processed_packages << pkg.name
94
-
95
- # Try to auto-exclude the package early. If the autobuild file
96
- # contained some information that allows us to exclude it now,
97
- # then let's just do it
98
- import_next_step(pkg, reverse_dependencies)
99
- if manifest.excluded?(pkg.name)
100
- selection.filter_excluded_and_ignored_packages(manifest)
101
- next
102
- elsif manifest.ignored?(pkg.name)
103
- next
104
- end
145
+ failures = Hash.new
146
+ while failures.empty? || ignore_errors
147
+ # Queue work for all packages in the queue
148
+ package_queue.each do |pkg|
149
+ # Remove packages that have already been processed
150
+ next if all_processed_packages.include?(pkg)
151
+ all_processed_packages << pkg
105
152
 
106
- # If the package has no importer, the source directory must
107
- # be there
108
- if !pkg.importer && !File.directory?(pkg.srcdir)
109
- raise ConfigError.new, "#{pkg.name} has no VCS, but is not checked out in #{pkg.srcdir}"
110
- end
153
+ if !pre_package_import(selection, manifest, pkg, reverse_dependencies)
154
+ next
155
+ elsif pkg.importer.interactive?
156
+ interactive_imports << pkg
157
+ next
158
+ end
111
159
 
112
- ## COMPLETELY BYPASS RAKE HERE
113
- # The reason is that the ordering of import/prepare between
114
- # packages is not important BUT the ordering of import vs.
115
- # prepare in one package IS important: prepare is the method
116
- # that takes into account dependencies.
117
- pkg.import(import_options)
118
- if pkg.updated?
119
- updated_packages << pkg.name
120
- end
121
- Rake::Task["#{pkg.name}-import"].instance_variable_set(:@already_invoked, true)
122
- manifest.load_package_manifest(pkg.name)
123
-
124
- # The package setup mechanisms might have added an exclusion
125
- # on this package. Handle this.
126
- if manifest.excluded?(pkg.name)
127
- mark_exclusion_along_revdeps(pkg.name, reverse_dependencies)
128
- # Run a filter now, to have errors as early as possible
129
- selection.filter_excluded_and_ignored_packages(manifest)
130
- # Delete this package from the current_packages set
131
- next
132
- elsif manifest.ignored?(pkg.name)
133
- next
160
+ pending_packages << pkg
161
+ import_future = Concurrent::Future.new(executor: executor, args: [pkg]) do |import_pkg|
162
+ ## COMPLETELY BYPASS RAKE HERE
163
+ # The reason is that the ordering of import/prepare between
164
+ # packages is not important BUT the ordering of import vs.
165
+ # prepare in one package IS important: prepare is the method
166
+ # that takes into account dependencies.
167
+ if retry_count
168
+ import_pkg.importer.retry_count = retry_count
169
+ end
170
+ import_pkg.import(import_options.merge(allow_interactive: false))
171
+ end
172
+ import_future.add_observer do |time, result, reason|
173
+ completion_queue << [pkg, time, result, reason]
174
+ end
175
+ import_future.execute
134
176
  end
177
+ package_queue.clear
135
178
 
136
- Autoproj.each_post_import_block(pkg) do |block|
137
- block.call(pkg)
179
+ if completion_queue.empty? && pending_packages.empty?
180
+ # We've nothing to process anymore ... process
181
+ # interactive imports if there are some. Otherwise,
182
+ # we're done
183
+ if interactive_imports.empty?
184
+ return all_processed_packages
185
+ else
186
+ interactive_imports.each do |pkg|
187
+ begin
188
+ result = pkg.import(import_options.merge(allow_interactive: true))
189
+ rescue Exception => reason
190
+ end
191
+ completion_queue << [pkg, Time.now, result, reason]
192
+ end
193
+ interactive_imports.clear
194
+ end
138
195
  end
139
196
 
140
- new_packages = import_next_step(pkg, reverse_dependencies)
141
-
142
- # Excluded dependencies might have caused the package to be
143
- # excluded as well ... do not add any dependency to the
144
- # processing queue if it is the case
145
- if manifest.excluded?(pkg.name)
146
- selection.filter_excluded_and_ignored_packages(manifest)
147
- elsif options[:recursive]
148
- package_queue.concat(new_packages.sort_by(&:name))
197
+ # And wait one to finish
198
+ pkg, time, result, reason = completion_queue.pop
199
+ pending_packages.delete(pkg)
200
+ if reason
201
+ if reason.kind_of?(Autobuild::InteractionRequired)
202
+ interactive_imports << pkg
203
+ else
204
+ # One importer failed... terminate
205
+ Autoproj.error "import of #{pkg.name} failed"
206
+ if !reason.kind_of?(Interrupt)
207
+ Autoproj.error "#{reason}"
208
+ end
209
+ failures[pkg] = reason
210
+ end
211
+ else
212
+ if new_packages = post_package_import(selection, manifest, pkg, reverse_dependencies)
213
+ # Excluded dependencies might have caused the package to be
214
+ # excluded as well ... do not add any dependency to the
215
+ # processing queue if it is the case
216
+ if manifest.excluded?(pkg.name)
217
+ selection.filter_excluded_and_ignored_packages(manifest)
218
+ elsif options[:recursive]
219
+ package_queue = new_packages.sort_by(&:name)
220
+ end
221
+ end
149
222
  end
150
223
  end
224
+
225
+ if !failures.empty?
226
+ raise ImportFailed, "import of #{failures.size} packages failed: #{failures.keys.map(&:name).sort.join(", ")}"
227
+ end
228
+
151
229
  all_processed_packages
230
+
231
+ ensure
232
+ if failures && !failures.empty? && !ignore_errors
233
+ Autoproj.error "waiting for pending import jobs to finish"
234
+ end
235
+ if executor
236
+ executor.shutdown
237
+ executor.wait_for_termination
238
+ end
239
+ updated_packages.concat(all_processed_packages.find_all(&:updated?).map(&:name))
152
240
  end
153
241
 
154
242
  def finalize_package_load(processed_packages)
@@ -164,7 +252,7 @@ module Autoproj
164
252
  next if manifest.ignored?(pkg_name) || manifest.excluded?(pkg_name)
165
253
 
166
254
  pkg = manifest.find_autobuild_package(pkg_name)
167
- if !processed_packages.include?(pkg.name)
255
+ if !processed_packages.include?(pkg)
168
256
  manifest.load_package_manifest(pkg.name)
169
257
  Autoproj.each_post_import_block(pkg) do |block|
170
258
  block.call(pkg)
@@ -187,6 +275,9 @@ module Autoproj
187
275
  end
188
276
 
189
277
  def import_packages(selection, options = Hash.new)
278
+ # Used in the ensure block, initialize as soon as possible
279
+ updated_packages = Array.new
280
+
190
281
  options, import_options = Kernel.filter_options options,
191
282
  warn_about_ignored_packages: true,
192
283
  warn_about_excluded_packages: true,
@@ -194,13 +285,17 @@ module Autoproj
194
285
 
195
286
  manifest = ws.manifest
196
287
 
197
- updated_packages = Array.new
198
288
  all_processed_packages = import_selected_packages(
199
289
  selection, updated_packages, import_options.merge(recursive: options[:recursive]))
200
290
  finalize_package_load(all_processed_packages)
201
291
 
202
292
  all_enabled_osdeps = selection.each_osdep_package_name.to_set
203
- all_enabled_sources = all_processed_packages
293
+ all_enabled_sources = all_processed_packages.map(&:name)
294
+ if options[:recursive]
295
+ all_processed_packages.each do |pkg|
296
+ all_enabled_osdeps.merge(pkg.os_packages)
297
+ end
298
+ end
204
299
 
205
300
  if options[:warn_about_excluded_packages]
206
301
  selection.exclusions.each do |sel, pkg_names|
@@ -225,7 +320,7 @@ module Autoproj
225
320
  if $!
226
321
  " (#{$!.message.split("\n").first})"
227
322
  end
228
- ops = Ops::Snapshot.new(ws.manifest, keep_going: true)
323
+ ops = Ops::Snapshot.new(ws.manifest, ignore_errors: true)
229
324
  ops.update_package_import_state(
230
325
  "#{$0} #{ARGV.join(" ")}#{failure_message}",
231
326
  updated_packages)