r10k 3.9.0 → 3.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +1 -1
  3. data/.github/workflows/stale.yml +19 -0
  4. data/CHANGELOG.mkd +5 -0
  5. data/doc/dynamic-environments/configuration.mkd +6 -6
  6. data/lib/r10k/action/base.rb +8 -1
  7. data/lib/r10k/action/deploy/display.rb +39 -9
  8. data/lib/r10k/action/deploy/environment.rb +63 -40
  9. data/lib/r10k/action/deploy/module.rb +47 -28
  10. data/lib/r10k/action/puppetfile/check.rb +3 -1
  11. data/lib/r10k/action/puppetfile/install.rb +20 -23
  12. data/lib/r10k/action/puppetfile/purge.rb +8 -2
  13. data/lib/r10k/content_synchronizer.rb +83 -0
  14. data/lib/r10k/deployment.rb +1 -1
  15. data/lib/r10k/environment/base.rb +21 -1
  16. data/lib/r10k/environment/git.rb +0 -3
  17. data/lib/r10k/environment/svn.rb +4 -6
  18. data/lib/r10k/environment/with_modules.rb +18 -10
  19. data/lib/r10k/module.rb +1 -1
  20. data/lib/r10k/module/base.rb +17 -1
  21. data/lib/r10k/module/forge.rb +24 -18
  22. data/lib/r10k/module/git.rb +22 -13
  23. data/lib/r10k/module/local.rb +1 -0
  24. data/lib/r10k/module/svn.rb +11 -8
  25. data/lib/r10k/puppetfile.rb +55 -70
  26. data/lib/r10k/source/base.rb +4 -0
  27. data/lib/r10k/source/git.rb +14 -6
  28. data/lib/r10k/source/hash.rb +1 -3
  29. data/lib/r10k/source/svn.rb +0 -2
  30. data/lib/r10k/util/cleaner.rb +21 -0
  31. data/lib/r10k/version.rb +1 -1
  32. data/locales/r10k.pot +51 -59
  33. data/spec/r10k-mocks/mock_source.rb +1 -1
  34. data/spec/shared-examples/puppetfile-action.rb +7 -7
  35. data/spec/unit/action/deploy/display_spec.rb +32 -6
  36. data/spec/unit/action/deploy/environment_spec.rb +76 -48
  37. data/spec/unit/action/deploy/module_spec.rb +139 -31
  38. data/spec/unit/action/puppetfile/check_spec.rb +2 -2
  39. data/spec/unit/action/puppetfile/install_spec.rb +31 -10
  40. data/spec/unit/action/puppetfile/purge_spec.rb +25 -5
  41. data/spec/unit/module/forge_spec.rb +15 -13
  42. data/spec/unit/module/git_spec.rb +8 -0
  43. data/spec/unit/module_spec.rb +5 -5
  44. data/spec/unit/puppetfile_spec.rb +40 -26
  45. data/spec/unit/util/purgeable_spec.rb +2 -8
  46. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e658209eb66330ab2c2cea4dd4fac0690b6700042e639e5ff11d2fffb120f1b
4
- data.tar.gz: 870249f76ef6c2b289e3255e6c752d6fc725009a8e37f8b4219f7496a7c0a439
3
+ metadata.gz: 8b0d4cbdba06f37b9a34638e82ba32b424db49e8047fabff52a9e661e5970e68
4
+ data.tar.gz: f5ac5bd9da0ef5de33780482d89732cab382c01e3f636839224bb02b7e636b7f
5
5
  SHA512:
6
- metadata.gz: a01dfae93025bac535c4e18d5f15b800c7bc1df1aa79cf0a2ea565c034767d5c5f0b0e4b41b7954e59310090a25ca67e28e92ab416958b2f91864a3da873d93a
7
- data.tar.gz: 0631fe1846185c2808bd14be2cca3b182402991476950bae18da0fb19129eb7139fbec3b1f9a3bf1568f09931d0438c9a014380378cce71b01d7b9022364cb7c
6
+ metadata.gz: 64e28d25bc71114d3b598376c2c5ee32d13d8ed998d50106fa1814defc8f463fac5dff1c57e5bafa1ef8b57d9f48d1e90b6460ee24a69de65759ff9b9a8c4722
7
+ data.tar.gz: 18caf3d9fff928ab9a3620daad375fabcb2a4ec5fbbf15f16a680d4ea4b724ce8cf3cb1cd2297d36bb81ca4384bfbbb85f1da71b219809a7f1ca1aa6aafb9f32
@@ -1,4 +1,4 @@
1
1
  Please add all notable changes to the "Unreleased" section of the CHANGELOG in the format:
2
2
  ```
3
- - Summary of changes. [Issue or PR #](link to issue or PR)
3
+ - (JIRA ticket) Summary of changes. [Issue or PR #](link to issue or PR)
4
4
  ```
@@ -0,0 +1,19 @@
1
+ name: Mark stale issues
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "30 1 * * *"
6
+
7
+ jobs:
8
+ stale:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/stale@v3
12
+ with:
13
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
14
+ days-before-stale: 60
15
+ days-before-close: 7
16
+ stale-issue-message: 'This issue has been marked stale because it has had no activity for 60 days. The Puppet Team is actively prioritizing existing bugs and new features, if this issue is still important to you please comment and we will add this to our backlog to complete. Otherwise, it will be closed in 7 days.'
17
+ stale-issue-label: 'stale'
18
+ stale-pr-message: "This PR has been marked stale because it has had no activity for 60 days. If you are still interested in getting this merged, please comment and we'll try to move it forward. Otherwise, it will be closed in 7 days."
19
+ stale-pr-label: 'stale'
data/CHANGELOG.mkd CHANGED
@@ -4,6 +4,11 @@ CHANGELOG
4
4
  Unreleased
5
5
  ----------
6
6
 
7
+ 3.9.1
8
+ -----
9
+
10
+ - Invalid module specifications in a Puppetfile will cause the R10K run to abort earlier than before. Prior to this release, the R10K run would complete, sync all other modules, and return an exit code of 1. R10K will now stop syncing modules and abort immediately. [#1161](https://github.com/puppetlabs/r10k/pull/1161)
11
+
7
12
  3.9.0
8
13
  -----
9
14
 
@@ -586,13 +586,13 @@ When using the YAML source type, every environment is enumerated in a single yam
586
586
  ---
587
587
  production:
588
588
  type: git
589
- remote: git@github.com:puppetlabs/control-repo.git
590
- ref: 8820892
589
+ source: git@github.com:puppetlabs/control-repo.git
590
+ version: 8820892
591
591
 
592
592
  development:
593
593
  type: git
594
- remote: git@github.com:puppetlabs/control-repo.git
595
- ref: 8820892
594
+ source: git@github.com:puppetlabs/control-repo.git
595
+ version: 8820892
596
596
  ```
597
597
 
598
598
  ### YAMLdir Environment Source
@@ -623,8 +623,8 @@ The contents of the file should be a hash specifying the enviornment type, and a
623
623
  # production.yaml
624
624
  ---
625
625
  type: git
626
- remote: git@github.com:puppetlabs/control-repo.git
627
- ref: 8820892
626
+ source: git@github.com:puppetlabs/control-repo.git
627
+ version: 8820892
628
628
  ```
629
629
 
630
630
  ### Exec environment Source
@@ -10,7 +10,14 @@ module R10K
10
10
 
11
11
  attr_accessor :settings
12
12
 
13
- def initialize(opts, argv, settings = {})
13
+ # @param opts [Hash] A hash of options defined in #allowed_initialized_opts
14
+ # and managed by the SetOps mixin within the Action::Base class.
15
+ # Corresponds to the CLI flags and options.
16
+ # @param argv [CRI::ArgumentList] A list-like collection of the remaining
17
+ # arguments to the CLI invocation (after removing flags and options).
18
+ # @param settings [Hash] A hash of configuration loaded from the relevant
19
+ # config (r10k.yaml).
20
+ def initialize(opts, argv, settings)
14
21
  @opts = opts
15
22
  @argv = argv
16
23
  @settings = settings
@@ -9,17 +9,45 @@ module R10K
9
9
 
10
10
  include R10K::Action::Deploy::DeployHelpers
11
11
 
12
+ # @param opts [Hash] A hash of options defined in #allowed_initialized_opts
13
+ # and managed by the SetOps mixin within the Action::Base class.
14
+ # Corresponds to the CLI flags and options.
15
+ # @param argv [CRI::ArgumentList] A list-like collection of the remaining
16
+ # arguments to the CLI invocation (after removing flags and options).
17
+ # @param settings [Hash] A hash of configuration loaded from the relevant
18
+ # config (r10k.yaml).
19
+ def initialize(opts, argv, settings)
20
+ super
21
+
22
+ @settings = @settings.merge({
23
+ overrides: {
24
+ environments: {
25
+ preload_environments: @fetch,
26
+ requested_environments: @argv.map { |arg| arg.gsub(/\W/, '_') }
27
+ },
28
+ modules: {},
29
+ output: {
30
+ format: @format,
31
+ trace: @trace,
32
+ detail: @detail
33
+ },
34
+ purging: {}
35
+ }
36
+ })
37
+ end
38
+
12
39
  def call
13
40
  expect_config!
14
41
  deployment = R10K::Deployment.new(@settings)
15
42
 
16
- if @fetch
43
+ if @settings.dig(:overrides, :environments, :preload_environments)
17
44
  deployment.preload!
45
+ deployment.validate!
18
46
  end
19
47
 
20
- output = { :sources => deployment.sources.map { |source| source_info(source, @argv) } }
48
+ output = { :sources => deployment.sources.map { |source| source_info(source, @settings.dig(:overrides, :environments, :requested_environments)) } }
21
49
 
22
- case @format
50
+ case @settings.dig(:overrides, :output, :format)
23
51
  when 'json' then json_format(output)
24
52
  else yaml_format(output)
25
53
  end
@@ -27,7 +55,7 @@ module R10K
27
55
  # exit 0
28
56
  true
29
57
  rescue => e
30
- logger.error R10K::Errors::Formatting.format_exception(e, @trace)
58
+ logger.error R10K::Errors::Formatting.format_exception(e, @settings.dig(:overrides, :output, :trace))
31
59
  false
32
60
  end
33
61
 
@@ -43,7 +71,7 @@ module R10K
43
71
  puts output.to_yaml
44
72
  end
45
73
 
46
- def source_info(source, argv=[])
74
+ def source_info(source, requested_environments = [])
47
75
  source_info = {
48
76
  :name => source.name,
49
77
  :basedir => source.basedir,
@@ -52,28 +80,30 @@ module R10K
52
80
  source_info[:prefix] = source.prefix if source.prefix
53
81
  source_info[:remote] = source.remote if source.respond_to?(:remote)
54
82
 
55
- env_list = source.environments.select { |env| argv.empty? || argv.include?(env.name) }
83
+ select_all_envs = requested_environments.empty?
84
+ env_list = source.environments.select { |env| select_all_envs || requested_environments.include?(env.name) }
56
85
  source_info[:environments] = env_list.map { |env| environment_info(env) }
57
86
 
58
87
  source_info
59
88
  end
60
89
 
61
90
  def environment_info(env)
62
- if !@modules && !@detail
91
+ modules = @settings.dig(:overrides, :environments, :deploy_modules)
92
+ if !modules && !@settings.dig(:overrides, :output, :detail)
63
93
  env.dirname
64
94
  else
65
95
  env_info = env.info.merge({
66
96
  :status => (env.status rescue nil),
67
97
  })
68
98
 
69
- env_info[:modules] = env.modules.map { |mod| module_info(mod) } if @modules
99
+ env_info[:modules] = env.modules.map { |mod| module_info(mod) } if modules
70
100
 
71
101
  env_info
72
102
  end
73
103
  end
74
104
 
75
105
  def module_info(mod)
76
- if @detail
106
+ if @settings.dig(:overrides, :output, :detail)
77
107
  { :name => mod.title, :properties => mod.properties }
78
108
  else
79
109
  mod.title
@@ -1,4 +1,5 @@
1
1
  require 'r10k/util/setopts'
2
+ require 'r10k/util/cleaner'
2
3
  require 'r10k/deployment'
3
4
  require 'r10k/logging'
4
5
  require 'r10k/action/visitor'
@@ -13,30 +14,61 @@ module R10K
13
14
 
14
15
  include R10K::Action::Deploy::DeployHelpers
15
16
 
17
+ # Deprecated
16
18
  attr_reader :force
17
19
 
18
- def initialize(opts, argv, settings = nil)
19
- settings ||= {}
20
- @purge_levels = settings.fetch(:deploy, {}).fetch(:purge_levels, [])
21
- @user_purge_allowlist = read_purge_allowlist(settings.fetch(:deploy, {}).fetch(:purge_whitelist, []),
22
- settings.fetch(:deploy, {}).fetch(:purge_allowlist, []))
23
- @generate_types = settings.fetch(:deploy, {}).fetch(:generate_types, false)
20
+ attr_reader :settings
24
21
 
22
+ # @param opts [Hash] A hash of options defined in #allowed_initialized_opts
23
+ # and managed by the SetOps mixin within the Action::Base class.
24
+ # Corresponds to the CLI flags and options.
25
+ # @param argv [CRI::ArgumentList] A list-like collection of the remaining
26
+ # arguments to the CLI invocation (after removing flags and options).
27
+ # @param settings [Hash] A hash of configuration loaded from the relevant
28
+ # config (r10k.yaml).
29
+ def initialize(opts, argv, settings)
25
30
  super
26
31
 
27
- # @force here is used to make it easier to reason about
28
- @force = !@no_force
29
- @argv = @argv.map { |arg| arg.gsub(/\W/,'_') }
32
+ # instance variables below are set by the super class based on the
33
+ # spec of #allowed_initialize_opts and any command line flags. This
34
+ # gives a preference order of cli flags > config files > defaults.
35
+ @settings = @settings.merge({
36
+ overrides: {
37
+ environments: {
38
+ requested_environments: @argv.map { |arg| arg.gsub(/\W/,'_') },
39
+ default_branch_override: @default_branch_override,
40
+ generate_types: @generate_types || settings.dig(:deploy, :generate_types) || false,
41
+ preload_environments: true
42
+ },
43
+ modules: {
44
+ requested_modules: [],
45
+ deploy_modules: @modules,
46
+ force: !@no_force, # force here is used to make it easier to reason about
47
+ },
48
+ purging: {
49
+ purge_levels: settings.dig(:deploy, :purge_levels) || [],
50
+ purge_allowlist: read_purge_allowlist(settings.dig(:deploy, :purge_whitelist) || [],
51
+ settings.dig(:deploy, :purge_allowlist) || [])
52
+ },
53
+ output: {}
54
+ }
55
+ })
30
56
  end
31
57
 
32
58
  def call
33
59
  @visit_ok = true
34
60
 
35
- expect_config!
36
- deployment = R10K::Deployment.new(@settings)
37
- check_write_lock!(@settings)
61
+ begin
62
+ expect_config!
63
+ deployment = R10K::Deployment.new(@settings)
64
+ check_write_lock!(@settings)
65
+
66
+ deployment.accept(self)
67
+ rescue => e
68
+ @visit_ok = false
69
+ logger.error R10K::Errors::Formatting.format_exception(e, @trace)
70
+ end
38
71
 
39
- deployment.accept(self)
40
72
  @visit_ok
41
73
  end
42
74
 
@@ -66,10 +98,12 @@ module R10K
66
98
  # sources then we can't fully enumerate all environments which
67
99
  # could be dangerous. If this fails then an exception will be raised
68
100
  # and execution will be halted.
69
- deployment.preload!
70
- deployment.validate!
101
+ if @settings.dig(:overrides, :environments, :preload_environments)
102
+ deployment.preload!
103
+ deployment.validate!
104
+ end
71
105
 
72
- undeployable = undeployable_environment_names(deployment.environments, @argv)
106
+ undeployable = undeployable_environment_names(deployment.environments, @settings.dig(:overrides, :environments, :requested_environments))
73
107
  if !undeployable.empty?
74
108
  @visit_ok = false
75
109
  logger.error _("Environment(s) \'%{environments}\' cannot be found in any source and will not be deployed.") % {environments: undeployable.join(", ")}
@@ -77,7 +111,7 @@ module R10K
77
111
 
78
112
  yield
79
113
 
80
- if @purge_levels.include?(:deployment)
114
+ if @settings.dig(:overrides, :purging, :purge_levels).include?(:deployment)
81
115
  logger.debug("Purging unmanaged environments for deployment...")
82
116
  deployment.purge!
83
117
  end
@@ -85,7 +119,8 @@ module R10K
85
119
  if (postcmd = @settings[:postrun])
86
120
  if postcmd.grep('$modifiedenvs').any?
87
121
  envs = deployment.environments.map { |e| e.dirname }
88
- envs.reject! { |e| !@argv.include?(e) } if @argv.any?
122
+ requested_envs = @settings.dig(:overrides, :environments, :requested_environments)
123
+ envs.reject! { |e| !requested_envs.include?(e) } if requested_envs.any?
89
124
  postcmd = postcmd.map { |e| e.gsub('$modifiedenvs', envs.join(' ')) }
90
125
  end
91
126
  subproc = R10K::Util::Subprocess.new(postcmd)
@@ -99,7 +134,8 @@ module R10K
99
134
  end
100
135
 
101
136
  def visit_environment(environment)
102
- if !(@argv.empty? || @argv.any? { |name| environment.dirname == name })
137
+ requested_envs = @settings.dig(:overrides, :environments, :requested_environments)
138
+ if !(requested_envs.empty? || requested_envs.any? { |name| environment.dirname == name })
103
139
  logger.debug1(_("Environment %{env_dir} does not match environment name filter, skipping") % {env_dir: environment.dirname})
104
140
  return
105
141
  end
@@ -113,28 +149,31 @@ module R10K
113
149
  environment.sync
114
150
  logger.info _("Environment %{env_dir} is now at %{env_signature}") % {env_dir: environment.dirname, env_signature: environment.signature}
115
151
 
116
- if status == :absent || @modules
152
+ if status == :absent || @settings.dig(:overrides, :modules, :deploy_modules)
117
153
  if status == :absent
118
154
  logger.debug(_("Environment %{env_dir} is new, updating all modules") % {env_dir: environment.dirname})
119
155
  end
120
156
 
121
157
  previous_ok = @visit_ok
122
158
  @visit_ok = true
123
- yield
159
+
160
+ environment.deploy
161
+
124
162
  @environment_ok = @visit_ok
125
163
  @visit_ok &&= previous_ok
126
164
  end
127
165
 
128
- if @purge_levels.include?(:environment)
166
+
167
+ if @settings.dig(:overrides, :purging, :purge_levels).include?(:environment)
129
168
  if @visit_ok
130
169
  logger.debug("Purging unmanaged content for environment '#{environment.dirname}'...")
131
- environment.purge!(:recurse => true, :whitelist => environment.whitelist(@user_purge_allowlist))
170
+ environment.purge!(:recurse => true, :whitelist => environment.whitelist(@settings.dig(:overrides, :purging, :purge_allowlist)))
132
171
  else
133
172
  logger.debug("Not purging unmanaged content for environment '#{environment.dirname}' due to prior deploy failures.")
134
173
  end
135
174
  end
136
175
 
137
- if @generate_types
176
+ if @settings.dig(:overrides, :environments, :generate_types)
138
177
  if @environment_ok
139
178
  logger.debug("Generating puppet types for environment '#{environment.dirname}'...")
140
179
  environment.generate_types!
@@ -146,22 +185,6 @@ module R10K
146
185
  write_environment_info!(environment, started_at, @visit_ok)
147
186
  end
148
187
 
149
- def visit_puppetfile(puppetfile)
150
- puppetfile.load(@opts[:'default-branch-override'])
151
-
152
- yield
153
-
154
- if @purge_levels.include?(:puppetfile)
155
- logger.debug("Purging unmanaged Puppetfile content for environment '#{puppetfile.environment.dirname}'...")
156
- puppetfile.purge!
157
- end
158
- end
159
-
160
- def visit_module(mod)
161
- logger.info _("Deploying %{origin} content %{path}") % {origin: mod.origin, path: mod.path}
162
- mod.sync(force: @force)
163
- end
164
-
165
188
  def write_environment_info!(environment, started_at, success)
166
189
  module_deploys =
167
190
  begin
@@ -10,25 +10,53 @@ module R10K
10
10
 
11
11
  include R10K::Action::Deploy::DeployHelpers
12
12
 
13
+ # Deprecated
13
14
  attr_reader :force
14
15
 
15
- def initialize(opts, argv, settings = nil)
16
- settings ||= {}
16
+ attr_reader :settings
17
17
 
18
+ # @param opts [Hash] A hash of options defined in #allowed_initialized_opts
19
+ # and managed by the SetOps mixin within the Action::Base class.
20
+ # Corresponds to the CLI flags and options.
21
+ # @param argv [CRI::ArgumentList] A list-like collection of the remaining
22
+ # arguments to the CLI invocation (after removing flags and options).
23
+ # @param settings [Hash] A hash of configuration loaded from the relevant
24
+ # config (r10k.yaml).
25
+ def initialize(opts, argv, settings)
18
26
  super
19
27
 
20
- # @force here is used to make it easier to reason about
21
- @force = !@no_force
28
+ requested_env = @opts[:environment] ? [@opts[:environment].gsub(/\W/, '_')] : []
29
+
30
+ @settings = @settings.merge({
31
+ overrides: {
32
+ environments: {
33
+ requested_environments: requested_env,
34
+ generate_types: @generate_types
35
+ },
36
+ modules: {
37
+ requested_modules: @argv.map.to_a,
38
+ # force here is used to make it easier to reason about
39
+ force: !@no_force
40
+ },
41
+ purging: {},
42
+ output: {}
43
+ }
44
+ })
22
45
  end
23
46
 
24
47
  def call
25
48
  @visit_ok = true
49
+ begin
50
+ expect_config!
51
+ deployment = R10K::Deployment.new(@settings)
52
+ check_write_lock!(@settings)
53
+
54
+ deployment.accept(self)
55
+ rescue => e
56
+ @visit_ok = false
57
+ logger.error R10K::Errors::Formatting.format_exception(e, @trace)
58
+ end
26
59
 
27
- expect_config!
28
- deployment = R10K::Deployment.new(@settings)
29
- check_write_lock!(@settings)
30
-
31
- deployment.accept(self)
32
60
  @visit_ok
33
61
  end
34
62
 
@@ -45,29 +73,20 @@ module R10K
45
73
  end
46
74
 
47
75
  def visit_environment(environment)
48
- if @opts[:environment] && (@opts[:environment] != environment.dirname)
49
- logger.debug1(_("Only updating modules in environment %{opt_env} skipping environment %{env_path}") % {opt_env: @opts[:environment], env_path: environment.path})
76
+ requested_envs = @settings.dig(:overrides, :environments, :requested_environments)
77
+ if !requested_envs.empty? && !requested_envs.include?(environment.dirname)
78
+ logger.debug1(_("Only updating modules in environment(s) %{opt_env} skipping environment %{env_path}") % {opt_env: requested_envs.inspect, env_path: environment.path})
50
79
  else
51
- logger.debug1(_("Updating modules %{modules} in environment %{env_path}") % {modules: @argv.inspect, env_path: environment.path})
52
- yield
53
- end
54
- end
80
+ logger.debug1(_("Updating modules %{modules} in environment %{env_path}") % {modules: @settings.dig(:overrides, :modules, :requested_modules).inspect, env_path: environment.path})
55
81
 
56
- def visit_puppetfile(puppetfile)
57
- puppetfile.load
58
- yield
59
- end
82
+ environment.deploy
60
83
 
61
- def visit_module(mod)
62
- if @argv.include?(mod.name)
63
- logger.info _("Deploying module %{mod_path}") % {mod_path: mod.path}
64
- mod.sync(force: @force)
65
- if mod.environment && @generate_types
66
- logger.debug("Generating puppet types for environment '#{mod.environment.dirname}'...")
67
- mod.environment.generate_types!
84
+ requested_mods = @settings.dig(:overrides, :modules, :requested_modules) || []
85
+ generate_types = @settings.dig(:overrides, :environments, :generate_types)
86
+ if generate_types && !((environment.modules.map(&:name) & requested_mods).empty?)
87
+ logger.debug("Generating puppet types for environment '#{environment.dirname}'...")
88
+ environment.generate_types!
68
89
  end
69
- else
70
- logger.debug1(_("Only updating modules %{modules}, skipping module %{mod_name}") % {modules: @argv.inspect, mod_name: mod.name})
71
90
  end
72
91
  end
73
92