bolt 2.31.0 → 2.32.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.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/Puppetfile +1 -1
 - data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
 - data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
 - data/guides/module.txt +19 -0
 - data/guides/modulepath.txt +25 -0
 - data/lib/bolt/config/options.rb +31 -13
 - data/lib/bolt/module_installer.rb +70 -115
 - data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
 - data/lib/bolt/module_installer/puppetfile.rb +117 -0
 - data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
 - data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
 - data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
 - data/lib/bolt/module_installer/resolver.rb +76 -0
 - data/lib/bolt/module_installer/specs.rb +93 -0
 - data/lib/bolt/module_installer/specs/forge_spec.rb +84 -0
 - data/lib/bolt/module_installer/specs/git_spec.rb +178 -0
 - data/lib/bolt/outputter.rb +0 -47
 - data/lib/bolt/outputter/human.rb +2 -2
 - data/lib/bolt/outputter/json.rb +1 -1
 - data/lib/bolt/project.rb +3 -8
 - data/lib/bolt/project_migrator/modules.rb +10 -8
 - data/lib/bolt/version.rb +1 -1
 - data/lib/bolt_server/schemas/partials/task.json +17 -2
 - metadata +21 -7
 - data/lib/bolt/puppetfile.rb +0 -149
 - data/lib/bolt/puppetfile/module.rb +0 -93
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 5b010e9146d3269d88005be58db6b788f57ea3046f96f28756641b0c7266eec2
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: ce8d15031143acabc664a7025ef004c69211c25316f9691783388ba74e61dec9
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: b45657eb2b985f8e97c59ff4603ad049fd17ee6585292463a57bde3b5c7ca55451883b5e88cb0f61e55c05f1d5415cbde3874320ae446124d646a3d3e8ffa812
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 673e3f3310bf4f22602153bf8300851fa3a832db8e73abc9352874bea7c0b1fd50a5baa42d18474885caea7ec9fad4aa50c25f44aa27974097e10ea03b31e07e
         
     | 
    
        data/Puppetfile
    CHANGED
    
    | 
         @@ -34,7 +34,7 @@ mod 'puppetlabs-stdlib', '6.5.0' 
     | 
|
| 
       34 
34 
     | 
    
         
             
            mod 'puppetlabs-aws_inventory', '0.5.2'
         
     | 
| 
       35 
35 
     | 
    
         
             
            mod 'puppetlabs-azure_inventory', '0.4.1'
         
     | 
| 
       36 
36 
     | 
    
         
             
            mod 'puppetlabs-gcloud_inventory', '0.1.3'
         
     | 
| 
       37 
     | 
    
         
            -
            mod 'puppetlabs-http_request', '0. 
     | 
| 
      
 37 
     | 
    
         
            +
            mod 'puppetlabs-http_request', '0.2.0'
         
     | 
| 
       38 
38 
     | 
    
         
             
            mod 'puppetlabs-pkcs7', '0.1.1'
         
     | 
| 
       39 
39 
     | 
    
         
             
            mod 'puppetlabs-terraform', '0.5.0'
         
     | 
| 
       40 
40 
     | 
    
         
             
            mod 'puppetlabs-vault', '0.3.0'
         
     | 
| 
         @@ -26,8 +26,51 @@ Puppet::Functions.create_function(:'out::message') do 
     | 
|
| 
       26 
26 
     | 
    
         
             
                # Send Analytics Report
         
     | 
| 
       27 
27 
     | 
    
         
             
                executor.report_function_call(self.class.name)
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                executor.publish_event(type: :message, message: message)
         
     | 
| 
      
 29 
     | 
    
         
            +
                executor.publish_event(type: :message, message: stringify(message))
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                nil
         
     | 
| 
       32 
32 
     | 
    
         
             
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              def stringify(message)
         
     | 
| 
      
 35 
     | 
    
         
            +
                formatted = format_message(message)
         
     | 
| 
      
 36 
     | 
    
         
            +
                if formatted.is_a?(Hash) || formatted.is_a?(Array)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  ::JSON.pretty_generate(formatted)
         
     | 
| 
      
 38 
     | 
    
         
            +
                else
         
     | 
| 
      
 39 
     | 
    
         
            +
                  formatted
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def format_message(message)
         
     | 
| 
      
 44 
     | 
    
         
            +
                case message
         
     | 
| 
      
 45 
     | 
    
         
            +
                when Array
         
     | 
| 
      
 46 
     | 
    
         
            +
                  message.map { |item| format_message(item) }
         
     | 
| 
      
 47 
     | 
    
         
            +
                when Bolt::ApplyResult
         
     | 
| 
      
 48 
     | 
    
         
            +
                  format_apply_result(message)
         
     | 
| 
      
 49 
     | 
    
         
            +
                when Bolt::Result, Bolt::ResultSet
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # This is equivalent to to_s, but formattable
         
     | 
| 
      
 51 
     | 
    
         
            +
                  message.to_data
         
     | 
| 
      
 52 
     | 
    
         
            +
                when Bolt::RunFailure
         
     | 
| 
      
 53 
     | 
    
         
            +
                  formatted_resultset = message.result_set.to_data
         
     | 
| 
      
 54 
     | 
    
         
            +
                  message.to_h.merge('result_set' => formatted_resultset)
         
     | 
| 
      
 55 
     | 
    
         
            +
                when Hash
         
     | 
| 
      
 56 
     | 
    
         
            +
                  message.each_with_object({}) do |(k, v), h|
         
     | 
| 
      
 57 
     | 
    
         
            +
                    h[format_message(k)] = format_message(v)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
                when Integer, Float, NilClass
         
     | 
| 
      
 60 
     | 
    
         
            +
                  message
         
     | 
| 
      
 61 
     | 
    
         
            +
                else
         
     | 
| 
      
 62 
     | 
    
         
            +
                  message.to_s
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              def format_apply_result(result)
         
     | 
| 
      
 67 
     | 
    
         
            +
                logs = result.resource_logs&.map do |log|
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # Omit low-level info/debug messages
         
     | 
| 
      
 69 
     | 
    
         
            +
                  next if %w[info debug].include?(log['level'])
         
     | 
| 
      
 70 
     | 
    
         
            +
                  indent(2, format_log(log))
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
                hash = result.to_data
         
     | 
| 
      
 73 
     | 
    
         
            +
                hash['logs'] = logs unless logs.empty?
         
     | 
| 
      
 74 
     | 
    
         
            +
                hash
         
     | 
| 
      
 75 
     | 
    
         
            +
              end
         
     | 
| 
       33 
76 
     | 
    
         
             
            end
         
     | 
| 
         @@ -9,11 +9,14 @@ Puppet::Functions.create_function(:prompt) do 
     | 
|
| 
       9 
9 
     | 
    
         
             
              # @param prompt The prompt to display.
         
     | 
| 
       10 
10 
     | 
    
         
             
              # @param options A hash of additional options.
         
     | 
| 
       11 
11 
     | 
    
         
             
              # @option options [Boolean] sensitive Disable echo back and mark the response as sensitive.
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   The returned value will be wrapped by the `Sensitive` data type. To access the raw
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   value, use the `unwrap` function (i.e. `$sensitive_value.unwrap`).
         
     | 
| 
       12 
14 
     | 
    
         
             
              # @return The response to the prompt.
         
     | 
| 
       13 
15 
     | 
    
         
             
              # @example Prompt the user if plan execution should continue
         
     | 
| 
       14 
16 
     | 
    
         
             
              #   $response = prompt('Continue executing plan? [Y\N]')
         
     | 
| 
       15 
17 
     | 
    
         
             
              # @example Prompt the user for sensitive information
         
     | 
| 
       16 
18 
     | 
    
         
             
              #   $password = prompt('Enter your password', 'sensitive' => true)
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   out::message("Password is: ${password.unwrap}")
         
     | 
| 
       17 
20 
     | 
    
         
             
              dispatch :prompt do
         
     | 
| 
       18 
21 
     | 
    
         
             
                param 'String', :prompt
         
     | 
| 
       19 
22 
     | 
    
         
             
                optional_param 'Hash[String[1], Any]', :options
         
     | 
    
        data/guides/module.txt
    ADDED
    
    | 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            TOPIC
         
     | 
| 
      
 2 
     | 
    
         
            +
                module
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            DESCRIPTION
         
     | 
| 
      
 5 
     | 
    
         
            +
                Modules are shareable, reusable packages of Puppet content. They can include
         
     | 
| 
      
 6 
     | 
    
         
            +
                tasks, plans, functions, and other types of content that you can use in your
         
     | 
| 
      
 7 
     | 
    
         
            +
                project. You can download and install modules to your project from the
         
     | 
| 
      
 8 
     | 
    
         
            +
                Puppet Forge or write your own modules. Bolt also ships with several helpful
         
     | 
| 
      
 9 
     | 
    
         
            +
                modules pre-installed that are available to all of your projects.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                Bolt makes it easy to manage the modules that your project depends on. You
         
     | 
| 
      
 12 
     | 
    
         
            +
                can use Bolt commands to install a project's modules, add new modules to a
         
     | 
| 
      
 13 
     | 
    
         
            +
                project, and view the modules that are available to the project.
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                To learn more about managing modules in a project, see the documentation.
         
     | 
| 
      
 16 
     | 
    
         
            +
                To learn how modules are loaded by Bolt, see the 'modulepath' guide.
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            DOCUMENTATION
         
     | 
| 
      
 19 
     | 
    
         
            +
                https://pup.pt/bolt-modules
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            TOPIC
         
     | 
| 
      
 2 
     | 
    
         
            +
                modulepath
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            DESCRIPTION
         
     | 
| 
      
 5 
     | 
    
         
            +
                The modulepath is an ordered list of directories that Bolt loads modules
         
     | 
| 
      
 6 
     | 
    
         
            +
                from. When Bolt runs a command, it automatically loads modules from the
         
     | 
| 
      
 7 
     | 
    
         
            +
                modulepath.
         
     | 
| 
      
 8 
     | 
    
         
            +
                
         
     | 
| 
      
 9 
     | 
    
         
            +
                While Bolt has a default modulepath, you can also configure your own
         
     | 
| 
      
 10 
     | 
    
         
            +
                modulepath, which can include directories within the project or directories
         
     | 
| 
      
 11 
     | 
    
         
            +
                elsewhere on your system. Regardless of whether your project uses a default
         
     | 
| 
      
 12 
     | 
    
         
            +
                or configured modulepath, Bolt automatically adds directories to the
         
     | 
| 
      
 13 
     | 
    
         
            +
                modulepath. This includes modules containing core Bolt content, which is
         
     | 
| 
      
 14 
     | 
    
         
            +
                added to the beginning of the modulepath, and bundled content, which is
         
     | 
| 
      
 15 
     | 
    
         
            +
                added to the end of the modulepath.
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                Modules loaded from a directory listed earlier in the modulepath take
         
     | 
| 
      
 18 
     | 
    
         
            +
                precedence over modules with the same name loaded from a directory later in
         
     | 
| 
      
 19 
     | 
    
         
            +
                the modulepath. Bolt will not warn or error when two modules share a name
         
     | 
| 
      
 20 
     | 
    
         
            +
                and instead will ignore modules with a lower precedence.
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                To learn more about modules, see the 'module' guide.
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            DOCUMENTATION
         
     | 
| 
      
 25 
     | 
    
         
            +
                https://pup.pt/bolt-project-reference#modulepath
         
     | 
    
        data/lib/bolt/config/options.rb
    CHANGED
    
    | 
         @@ -234,26 +234,44 @@ module Bolt 
     | 
|
| 
       234 
234 
     | 
    
         
             
                      type: Array,
         
     | 
| 
       235 
235 
     | 
    
         
             
                      items: {
         
     | 
| 
       236 
236 
     | 
    
         
             
                        type: [Hash, String],
         
     | 
| 
       237 
     | 
    
         
            -
                         
     | 
| 
       238 
     | 
    
         
            -
             
     | 
| 
       239 
     | 
    
         
            -
             
     | 
| 
       240 
     | 
    
         
            -
                             
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
      
 237 
     | 
    
         
            +
                        oneOf: [
         
     | 
| 
      
 238 
     | 
    
         
            +
                          {
         
     | 
| 
      
 239 
     | 
    
         
            +
                            required: ["name"],
         
     | 
| 
      
 240 
     | 
    
         
            +
                            properties: {
         
     | 
| 
      
 241 
     | 
    
         
            +
                              "name" => {
         
     | 
| 
      
 242 
     | 
    
         
            +
                                description: "The name of the module.",
         
     | 
| 
      
 243 
     | 
    
         
            +
                                type: String
         
     | 
| 
      
 244 
     | 
    
         
            +
                              },
         
     | 
| 
      
 245 
     | 
    
         
            +
                              "version_requirement" => {
         
     | 
| 
      
 246 
     | 
    
         
            +
                                description: "The version requirement for the module. Accepts a specific version (1.2.3), version "\
         
     | 
| 
      
 247 
     | 
    
         
            +
                                             "shorthand (1.2.x), or a version range (>= 1.2.0).",
         
     | 
| 
      
 248 
     | 
    
         
            +
                                type: String
         
     | 
| 
      
 249 
     | 
    
         
            +
                              }
         
     | 
| 
      
 250 
     | 
    
         
            +
                            }
         
     | 
| 
       242 
251 
     | 
    
         
             
                          },
         
     | 
| 
       243 
     | 
    
         
            -
                           
     | 
| 
       244 
     | 
    
         
            -
                             
     | 
| 
       245 
     | 
    
         
            -
             
     | 
| 
       246 
     | 
    
         
            -
             
     | 
| 
      
 252 
     | 
    
         
            +
                          {
         
     | 
| 
      
 253 
     | 
    
         
            +
                            required: %w[git ref],
         
     | 
| 
      
 254 
     | 
    
         
            +
                            properties: {
         
     | 
| 
      
 255 
     | 
    
         
            +
                              "git" => {
         
     | 
| 
      
 256 
     | 
    
         
            +
                                description: "The URL to the public git repository.",
         
     | 
| 
      
 257 
     | 
    
         
            +
                                type: String
         
     | 
| 
      
 258 
     | 
    
         
            +
                              },
         
     | 
| 
      
 259 
     | 
    
         
            +
                              "ref" => {
         
     | 
| 
      
 260 
     | 
    
         
            +
                                description: "The git reference to check out. Can be either a branch, tag, or commit SHA.",
         
     | 
| 
      
 261 
     | 
    
         
            +
                                type: String
         
     | 
| 
      
 262 
     | 
    
         
            +
                              }
         
     | 
| 
      
 263 
     | 
    
         
            +
                            }
         
     | 
| 
       247 
264 
     | 
    
         
             
                          }
         
     | 
| 
       248 
     | 
    
         
            -
                         
     | 
| 
      
 265 
     | 
    
         
            +
                        ]
         
     | 
| 
       249 
266 
     | 
    
         
             
                      },
         
     | 
| 
       250 
267 
     | 
    
         
             
                      _plugin: false,
         
     | 
| 
       251 
268 
     | 
    
         
             
                      _example: [
         
     | 
| 
       252 
     | 
    
         
            -
                        { "name" => "puppetlabs-mysql" },
         
     | 
| 
       253 
269 
     | 
    
         
             
                        "puppetlabs-facts",
         
     | 
| 
      
 270 
     | 
    
         
            +
                        { "name" => "puppetlabs-mysql" },
         
     | 
| 
       254 
271 
     | 
    
         
             
                        { "name" => "puppetlabs-apache", "version_requirement" => "5.5.0" },
         
     | 
| 
       255 
272 
     | 
    
         
             
                        { "name" => "puppetlabs-puppetdb", "version_requirement" => "7.x" },
         
     | 
| 
       256 
     | 
    
         
            -
                        { "name" => "puppetlabs-firewall", "version_requirement" => ">= 1.0.0 < 3.0.0" }
         
     | 
| 
      
 273 
     | 
    
         
            +
                        { "name" => "puppetlabs-firewall", "version_requirement" => ">= 1.0.0 < 3.0.0" },
         
     | 
| 
      
 274 
     | 
    
         
            +
                        { "git" => "https://github.com/puppetlabs/puppetlabs-apt", "ref" => "7.6.0" }
         
     | 
| 
       257 
275 
     | 
    
         
             
                      ]
         
     | 
| 
       258 
276 
     | 
    
         
             
                    },
         
     | 
| 
       259 
277 
     | 
    
         
             
                    "name" => {
         
     | 
| 
         @@ -329,7 +347,7 @@ module Bolt 
     | 
|
| 
       329 
347 
     | 
    
         
             
                        "server_urls" => {
         
     | 
| 
       330 
348 
     | 
    
         
             
                          description: "An array containing the PuppetDB host to connect to. Include the protocol `https` "\
         
     | 
| 
       331 
349 
     | 
    
         
             
                                       "and the port, which is usually `8081`. For example, "\
         
     | 
| 
       332 
     | 
    
         
            -
                                       "`https://my- 
     | 
| 
      
 350 
     | 
    
         
            +
                                       "`https://my-puppetdb-server.com:8081`.",
         
     | 
| 
       333 
351 
     | 
    
         
             
                          type: Array,
         
     | 
| 
       334 
352 
     | 
    
         
             
                          _example: ["https://puppet.example.com:8081"]
         
     | 
| 
       335 
353 
     | 
    
         
             
                        },
         
     | 
| 
         @@ -2,6 +2,10 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'bolt/error'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'bolt/logger'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'bolt/module_installer/installer'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'bolt/module_installer/puppetfile'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'bolt/module_installer/resolver'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'bolt/module_installer/specs'
         
     | 
| 
       5 
9 
     | 
    
         | 
| 
       6 
10 
     | 
    
         
             
            module Bolt
         
     | 
| 
       7 
11 
     | 
    
         
             
              class ModuleInstaller
         
     | 
| 
         @@ -13,45 +17,53 @@ module Bolt 
     | 
|
| 
       13 
17 
     | 
    
         | 
| 
       14 
18 
     | 
    
         
             
                # Adds a single module to the project.
         
     | 
| 
       15 
19 
     | 
    
         
             
                #
         
     | 
| 
       16 
     | 
    
         
            -
                def add(name,  
     | 
| 
       17 
     | 
    
         
            -
                   
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                   
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                  new_module  = Bolt::Puppetfile::Module.from_hash('name' => name)
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                  if puppetfile.modules.include?(new_module)
         
     | 
| 
       27 
     | 
    
         
            -
                    @outputter.print_action_step(
         
     | 
| 
       28 
     | 
    
         
            -
                      "Project configuration file #{config_path} already includes module #{new_module}. Nothing to do."
         
     | 
| 
      
 20 
     | 
    
         
            +
                def add(name, specs, puppetfile_path, moduledir, config_path)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  project_specs = Specs.new(specs)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # Exit early if project config already includes a spec with this name.
         
     | 
| 
      
 24 
     | 
    
         
            +
                  if project_specs.include?(name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @outputter.print_message(
         
     | 
| 
      
 26 
     | 
    
         
            +
                      "Project configuration file #{config_path} already includes specification with name "\
         
     | 
| 
      
 27 
     | 
    
         
            +
                      "#{name}. Nothing to do."
         
     | 
| 
       29 
28 
     | 
    
         
             
                    )
         
     | 
| 
       30 
29 
     | 
    
         
             
                    return true
         
     | 
| 
       31 
30 
     | 
    
         
             
                  end
         
     | 
| 
       32 
31 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
       34 
     | 
    
         
            -
                  if puppetfile_path.exist?
         
     | 
| 
       35 
     | 
    
         
            -
                    assert_managed_puppetfile(puppetfile, puppetfile_path)
         
     | 
| 
       36 
     | 
    
         
            -
                    existing = Bolt::Puppetfile.parse(puppetfile_path)
         
     | 
| 
       37 
     | 
    
         
            -
                  else
         
     | 
| 
       38 
     | 
    
         
            -
                    existing = Bolt::Puppetfile.new
         
     | 
| 
       39 
     | 
    
         
            -
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @outputter.print_message("Adding module #{name} to project\n\n")
         
     | 
| 
       40 
33 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
                  #  
     | 
| 
       42 
     | 
    
         
            -
                  #  
     | 
| 
       43 
     | 
    
         
            -
                   
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
      
 34 
     | 
    
         
            +
                  # Generate the specs to resolve from. If a Puppetfile exists, parse it and
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # convert the modules to specs. Otherwise, use the project specs.
         
     | 
| 
      
 36 
     | 
    
         
            +
                  resolve_specs = if puppetfile_path.exist?
         
     | 
| 
      
 37 
     | 
    
         
            +
                                    existing_puppetfile = Puppetfile.parse(puppetfile_path)
         
     | 
| 
      
 38 
     | 
    
         
            +
                                    existing_puppetfile.assert_satisfies(project_specs)
         
     | 
| 
      
 39 
     | 
    
         
            +
                                    Specs.from_puppetfile(existing_puppetfile)
         
     | 
| 
      
 40 
     | 
    
         
            +
                                  else
         
     | 
| 
      
 41 
     | 
    
         
            +
                                    project_specs
         
     | 
| 
      
 42 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # Resolve module dependencies. Attempt to first resolve with resolve
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # specss. If that fails, fall back to resolving from project specs.
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # This prevents Bolt from modifying installed modules unless there is
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # a version conflict.
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @outputter.print_action_step("Resolving module dependencies, this may take a moment")
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 51 
     | 
    
         
            +
                    resolve_specs.add_specs('name' => name)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    puppetfile = Resolver.new.resolve(resolve_specs)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  rescue Bolt::Error
         
     | 
| 
      
 54 
     | 
    
         
            +
                    project_specs.add_specs('name' => name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    puppetfile = Resolver.new.resolve(project_specs)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
       45 
57 
     | 
    
         | 
| 
       46 
58 
     | 
    
         
             
                  # Display the diff between the existing Puppetfile and the new Puppetfile.
         
     | 
| 
       47 
     | 
    
         
            -
                  print_puppetfile_diff( 
     | 
| 
      
 59 
     | 
    
         
            +
                  print_puppetfile_diff(existing_puppetfile, puppetfile)
         
     | 
| 
       48 
60 
     | 
    
         | 
| 
       49 
61 
     | 
    
         
             
                  # Add the module to the project configuration.
         
     | 
| 
       50 
62 
     | 
    
         
             
                  @outputter.print_action_step("Updating project configuration file at #{config_path}")
         
     | 
| 
       51 
63 
     | 
    
         | 
| 
       52 
64 
     | 
    
         
             
                  data = Bolt::Util.read_yaml_hash(config_path, 'project')
         
     | 
| 
       53 
65 
     | 
    
         
             
                  data['modules'] ||= []
         
     | 
| 
       54 
     | 
    
         
            -
                  data['modules'] << 
     | 
| 
      
 66 
     | 
    
         
            +
                  data['modules'] << name
         
     | 
| 
       55 
67 
     | 
    
         | 
| 
       56 
68 
     | 
    
         
             
                  begin
         
     | 
| 
       57 
69 
     | 
    
         
             
                    File.write(config_path, data.to_yaml)
         
     | 
| 
         @@ -70,130 +82,97 @@ module Bolt 
     | 
|
| 
       70 
82 
     | 
    
         
             
                  install_puppetfile(puppetfile_path, moduledir)
         
     | 
| 
       71 
83 
     | 
    
         
             
                end
         
     | 
| 
       72 
84 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
                # Creates a new Puppetfile that includes the new module and its dependencies.
         
     | 
| 
       74 
     | 
    
         
            -
                #
         
     | 
| 
       75 
     | 
    
         
            -
                private def add_new_module_to_puppetfile(new_module, modules, path)
         
     | 
| 
       76 
     | 
    
         
            -
                  @outputter.print_action_step("Resolving module dependencies, this may take a moment")
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                  # If there is an existing Puppetfile, add the new module and attempt
         
     | 
| 
       79 
     | 
    
         
            -
                  # to resolve. This will not update the versions of any installed modules.
         
     | 
| 
       80 
     | 
    
         
            -
                  if path.exist?
         
     | 
| 
       81 
     | 
    
         
            -
                    puppetfile = Bolt::Puppetfile.parse(path)
         
     | 
| 
       82 
     | 
    
         
            -
                    puppetfile.add_modules(new_module)
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                    begin
         
     | 
| 
       85 
     | 
    
         
            -
                      puppetfile.resolve
         
     | 
| 
       86 
     | 
    
         
            -
                      return puppetfile
         
     | 
| 
       87 
     | 
    
         
            -
                    rescue Bolt::Error
         
     | 
| 
       88 
     | 
    
         
            -
                      @logger.debug "Unable to find a version of #{new_module} compatible "\
         
     | 
| 
       89 
     | 
    
         
            -
                                    "with installed modules. Attempting to re-resolve modules "\
         
     | 
| 
       90 
     | 
    
         
            -
                                    "from project configuration; some versions of installed "\
         
     | 
| 
       91 
     | 
    
         
            -
                                    "modules may change."
         
     | 
| 
       92 
     | 
    
         
            -
                    end
         
     | 
| 
       93 
     | 
    
         
            -
                  end
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                  # If there is not an existing Puppetfile, or resolving with pinned
         
     | 
| 
       96 
     | 
    
         
            -
                  # modules fails, resolve all of the module declarations with the new
         
     | 
| 
       97 
     | 
    
         
            -
                  # module.
         
     | 
| 
       98 
     | 
    
         
            -
                  puppetfile = Bolt::Puppetfile.new(modules)
         
     | 
| 
       99 
     | 
    
         
            -
                  puppetfile.add_modules(new_module)
         
     | 
| 
       100 
     | 
    
         
            -
                  puppetfile.resolve
         
     | 
| 
       101 
     | 
    
         
            -
                  puppetfile
         
     | 
| 
       102 
     | 
    
         
            -
                end
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
85 
     | 
    
         
             
                # Outputs a diff of an old Puppetfile and a new Puppetfile.
         
     | 
| 
       105 
86 
     | 
    
         
             
                #
         
     | 
| 
       106 
87 
     | 
    
         
             
                def print_puppetfile_diff(old, new)
         
     | 
| 
       107 
     | 
    
         
            -
                  # Build hashes mapping the module  
     | 
| 
      
 88 
     | 
    
         
            +
                  # Build hashes mapping the module name to the module object. This makes it
         
     | 
| 
       108 
89 
     | 
    
         
             
                  # a little easier to determine which modules have been added, removed, or
         
     | 
| 
       109 
90 
     | 
    
         
             
                  # modified.
         
     | 
| 
       110 
     | 
    
         
            -
                  old = old 
     | 
| 
       111 
     | 
    
         
            -
                     
     | 
| 
      
 91 
     | 
    
         
            +
                  old = (old&.modules || []).each_with_object({}) do |mod, acc|
         
     | 
| 
      
 92 
     | 
    
         
            +
                    next unless mod.type == :forge
         
     | 
| 
      
 93 
     | 
    
         
            +
                    acc[mod.full_name] = mod
         
     | 
| 
       112 
94 
     | 
    
         
             
                  end
         
     | 
| 
       113 
95 
     | 
    
         | 
| 
       114 
96 
     | 
    
         
             
                  new = new.modules.each_with_object({}) do |mod, acc|
         
     | 
| 
       115 
     | 
    
         
            -
                     
     | 
| 
      
 97 
     | 
    
         
            +
                    next unless mod.type == :forge
         
     | 
| 
      
 98 
     | 
    
         
            +
                    acc[mod.full_name] = mod
         
     | 
| 
       116 
99 
     | 
    
         
             
                  end
         
     | 
| 
       117 
100 
     | 
    
         | 
| 
       118 
101 
     | 
    
         
             
                  # New modules are those present in new but not in old.
         
     | 
| 
       119 
     | 
    
         
            -
                  added = new.reject { | 
     | 
| 
      
 102 
     | 
    
         
            +
                  added = new.reject { |full_name, _mod| old.include?(full_name) }.values
         
     | 
| 
       120 
103 
     | 
    
         | 
| 
       121 
104 
     | 
    
         
             
                  if added.any?
         
     | 
| 
       122 
105 
     | 
    
         
             
                    diff = "Adding the following modules:\n"
         
     | 
| 
       123 
     | 
    
         
            -
                    added.each { |mod| diff += "#{mod. 
     | 
| 
      
 106 
     | 
    
         
            +
                    added.each { |mod| diff += "#{mod.full_name} #{mod.version}\n" }
         
     | 
| 
       124 
107 
     | 
    
         
             
                    @outputter.print_action_step(diff)
         
     | 
| 
       125 
108 
     | 
    
         
             
                  end
         
     | 
| 
       126 
109 
     | 
    
         | 
| 
       127 
110 
     | 
    
         
             
                  # Upgraded modules are those that have a newer version in new than old.
         
     | 
| 
       128 
     | 
    
         
            -
                  upgraded = new.select do | 
     | 
| 
       129 
     | 
    
         
            -
                    if old.include?( 
     | 
| 
       130 
     | 
    
         
            -
                       
     | 
| 
      
 111 
     | 
    
         
            +
                  upgraded = new.select do |full_name, mod|
         
     | 
| 
      
 112 
     | 
    
         
            +
                    if old.include?(full_name)
         
     | 
| 
      
 113 
     | 
    
         
            +
                      mod.version > old[full_name].version
         
     | 
| 
       131 
114 
     | 
    
         
             
                    end
         
     | 
| 
       132 
115 
     | 
    
         
             
                  end.keys
         
     | 
| 
       133 
116 
     | 
    
         | 
| 
       134 
117 
     | 
    
         
             
                  if upgraded.any?
         
     | 
| 
       135 
118 
     | 
    
         
             
                    diff = "Upgrading the following modules:\n"
         
     | 
| 
       136 
     | 
    
         
            -
                    upgraded.each { | 
     | 
| 
      
 119 
     | 
    
         
            +
                    upgraded.each { |full_name| diff += "#{full_name} #{old[full_name].version} to #{new[full_name].version}\n" }
         
     | 
| 
       137 
120 
     | 
    
         
             
                    @outputter.print_action_step(diff)
         
     | 
| 
       138 
121 
     | 
    
         
             
                  end
         
     | 
| 
       139 
122 
     | 
    
         | 
| 
       140 
123 
     | 
    
         
             
                  # Downgraded modules are those that have an older version in new than old.
         
     | 
| 
       141 
     | 
    
         
            -
                  downgraded = new.select do | 
     | 
| 
       142 
     | 
    
         
            -
                    if old.include?( 
     | 
| 
       143 
     | 
    
         
            -
                       
     | 
| 
      
 124 
     | 
    
         
            +
                  downgraded = new.select do |full_name, mod|
         
     | 
| 
      
 125 
     | 
    
         
            +
                    if old.include?(full_name)
         
     | 
| 
      
 126 
     | 
    
         
            +
                      mod.version < old[full_name].version
         
     | 
| 
       144 
127 
     | 
    
         
             
                    end
         
     | 
| 
       145 
128 
     | 
    
         
             
                  end.keys
         
     | 
| 
       146 
129 
     | 
    
         | 
| 
       147 
130 
     | 
    
         
             
                  if downgraded.any?
         
     | 
| 
       148 
131 
     | 
    
         
             
                    diff = "Downgrading the following modules: \n"
         
     | 
| 
       149 
     | 
    
         
            -
                    downgraded.each { | 
     | 
| 
      
 132 
     | 
    
         
            +
                    downgraded.each { |full_name| diff += "#{full_name} #{old[full_name].version} to #{new[full_name].version}\n" }
         
     | 
| 
       150 
133 
     | 
    
         
             
                    @outputter.print_action_step(diff)
         
     | 
| 
       151 
134 
     | 
    
         
             
                  end
         
     | 
| 
       152 
135 
     | 
    
         | 
| 
       153 
136 
     | 
    
         
             
                  # Removed modules are those present in old but not in new.
         
     | 
| 
       154 
     | 
    
         
            -
                  removed = old.reject { | 
     | 
| 
      
 137 
     | 
    
         
            +
                  removed = old.reject { |full_name, _mod| new.include?(full_name) }.values
         
     | 
| 
       155 
138 
     | 
    
         | 
| 
       156 
139 
     | 
    
         
             
                  if removed.any?
         
     | 
| 
       157 
140 
     | 
    
         
             
                    diff = "Removing the following modules:\n"
         
     | 
| 
       158 
     | 
    
         
            -
                    removed.each { |mod| diff += "#{mod. 
     | 
| 
      
 141 
     | 
    
         
            +
                    removed.each { |mod| diff += "#{mod.full_name} #{mod.version}\n" }
         
     | 
| 
       159 
142 
     | 
    
         
             
                    @outputter.print_action_step(diff)
         
     | 
| 
       160 
143 
     | 
    
         
             
                  end
         
     | 
| 
       161 
144 
     | 
    
         
             
                end
         
     | 
| 
       162 
145 
     | 
    
         | 
| 
       163 
146 
     | 
    
         
             
                # Installs a project's module dependencies.
         
     | 
| 
       164 
147 
     | 
    
         
             
                #
         
     | 
| 
       165 
     | 
    
         
            -
                def install( 
     | 
| 
       166 
     | 
    
         
            -
                  require 'bolt/puppetfile'
         
     | 
| 
       167 
     | 
    
         
            -
             
     | 
| 
      
 148 
     | 
    
         
            +
                def install(specs, path, moduledir, force: false, resolve: true)
         
     | 
| 
       168 
149 
     | 
    
         
             
                  @outputter.print_message("Installing project modules\n\n")
         
     | 
| 
       169 
150 
     | 
    
         | 
| 
       170 
     | 
    
         
            -
                  puppetfile = Bolt::Puppetfile.new(modules)
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
                  # If the Puppetfile exists, check if it includes specs for each declared
         
     | 
| 
       173 
     | 
    
         
            -
                  # module, erroring if there are any missing. Otherwise, resolve the
         
     | 
| 
       174 
     | 
    
         
            -
                  # module dependencies and write a new Puppetfile. Users can forcibly
         
     | 
| 
       175 
     | 
    
         
            -
                  # overwrite an existing Puppetfile with the '--force' option, or opt to
         
     | 
| 
       176 
     | 
    
         
            -
                  # install the Puppetfile as-is with --no-resolve.
         
     | 
| 
       177 
     | 
    
         
            -
                  #
         
     | 
| 
       178 
     | 
    
         
            -
                  # This is just if resolve is not false (nil should default to true)
         
     | 
| 
       179 
151 
     | 
    
         
             
                  if resolve != false
         
     | 
| 
       180 
     | 
    
         
            -
                     
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
                     
     | 
| 
      
 152 
     | 
    
         
            +
                    specs = Specs.new(specs)
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                    # If forcibly installing or if there is no Puppetfile, resolve
         
     | 
| 
      
 155 
     | 
    
         
            +
                    # and write a Puppetfile.
         
     | 
| 
      
 156 
     | 
    
         
            +
                    if force || !path.exist?
         
     | 
| 
       183 
157 
     | 
    
         
             
                      @outputter.print_action_step("Resolving module dependencies, this may take a moment")
         
     | 
| 
       184 
     | 
    
         
            -
                      puppetfile.resolve
         
     | 
| 
      
 158 
     | 
    
         
            +
                      puppetfile = Resolver.new.resolve(specs)
         
     | 
| 
       185 
159 
     | 
    
         | 
| 
       186 
     | 
    
         
            -
                      @outputter.print_action_step("Writing Puppetfile at #{path}")
         
     | 
| 
       187 
160 
     | 
    
         
             
                      # We get here either through 'bolt module install' which uses the
         
     | 
| 
       188 
161 
     | 
    
         
             
                      # managed modulepath (which isn't configurable) or through bolt
         
     | 
| 
       189 
162 
     | 
    
         
             
                      # project init --modules, which uses the default modulepath. This
         
     | 
| 
       190 
163 
     | 
    
         
             
                      # should be safe to assume that if `.modules/` is the moduledir the
         
     | 
| 
       191 
164 
     | 
    
         
             
                      # user is using the new workflow
         
     | 
| 
      
 165 
     | 
    
         
            +
                      @outputter.print_action_step("Writing Puppetfile at #{path}")
         
     | 
| 
       192 
166 
     | 
    
         
             
                      if moduledir.basename.to_s == '.modules'
         
     | 
| 
       193 
167 
     | 
    
         
             
                        puppetfile.write(path, moduledir)
         
     | 
| 
       194 
168 
     | 
    
         
             
                      else
         
     | 
| 
       195 
169 
     | 
    
         
             
                        puppetfile.write(path)
         
     | 
| 
       196 
170 
     | 
    
         
             
                      end
         
     | 
| 
      
 171 
     | 
    
         
            +
                    # If not forcibly installing and there is a Puppetfile, assert
         
     | 
| 
      
 172 
     | 
    
         
            +
                    # that it satisfies the specs.
         
     | 
| 
      
 173 
     | 
    
         
            +
                    else
         
     | 
| 
      
 174 
     | 
    
         
            +
                      puppetfile = Puppetfile.parse(path)
         
     | 
| 
      
 175 
     | 
    
         
            +
                      puppetfile.assert_satisfies(specs)
         
     | 
| 
       197 
176 
     | 
    
         
             
                    end
         
     | 
| 
       198 
177 
     | 
    
         
             
                  end
         
     | 
| 
       199 
178 
     | 
    
         | 
| 
         @@ -204,10 +183,8 @@ module Bolt 
     | 
|
| 
       204 
183 
     | 
    
         
             
                # Installs the Puppetfile and generates types.
         
     | 
| 
       205 
184 
     | 
    
         
             
                #
         
     | 
| 
       206 
185 
     | 
    
         
             
                def install_puppetfile(path, moduledir, config = {})
         
     | 
| 
       207 
     | 
    
         
            -
                  require 'bolt/puppetfile/installer'
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
186 
     | 
    
         
             
                  @outputter.print_action_step("Syncing modules from #{path} to #{moduledir}")
         
     | 
| 
       210 
     | 
    
         
            -
                  ok =  
     | 
| 
      
 187 
     | 
    
         
            +
                  ok = Installer.new(config).install(path, moduledir)
         
     | 
| 
       211 
188 
     | 
    
         | 
| 
       212 
189 
     | 
    
         
             
                  # Automatically generate types after installing modules
         
     | 
| 
       213 
190 
     | 
    
         
             
                  @pal.generate_types
         
     | 
| 
         @@ -216,27 +193,5 @@ module Bolt 
     | 
|
| 
       216 
193 
     | 
    
         | 
| 
       217 
194 
     | 
    
         
             
                  ok
         
     | 
| 
       218 
195 
     | 
    
         
             
                end
         
     | 
| 
       219 
     | 
    
         
            -
             
     | 
| 
       220 
     | 
    
         
            -
                # Asserts that an existing Puppetfile is managed by Bolt.
         
     | 
| 
       221 
     | 
    
         
            -
                #
         
     | 
| 
       222 
     | 
    
         
            -
                private def assert_managed_puppetfile(puppetfile, path)
         
     | 
| 
       223 
     | 
    
         
            -
                  existing_puppetfile = Bolt::Puppetfile.parse(path)
         
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
       225 
     | 
    
         
            -
                  unless existing_puppetfile.modules.superset? puppetfile.modules
         
     | 
| 
       226 
     | 
    
         
            -
                    missing_modules = puppetfile.modules - existing_puppetfile.modules
         
     | 
| 
       227 
     | 
    
         
            -
             
     | 
| 
       228 
     | 
    
         
            -
                    message = <<~MESSAGE.chomp
         
     | 
| 
       229 
     | 
    
         
            -
                      Puppetfile #{path} is missing specifications for the following
         
     | 
| 
       230 
     | 
    
         
            -
                      module declarations:
         
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
       232 
     | 
    
         
            -
                      #{missing_modules.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
         
     | 
| 
       233 
     | 
    
         
            -
                      
         
     | 
| 
       234 
     | 
    
         
            -
                      This may not be a Puppetfile managed by Bolt. To forcibly overwrite the
         
     | 
| 
       235 
     | 
    
         
            -
                      Puppetfile, run 'bolt module install --force'.
         
     | 
| 
       236 
     | 
    
         
            -
                    MESSAGE
         
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
       238 
     | 
    
         
            -
                    raise Bolt::Error.new(message, 'bolt/missing-module-specs')
         
     | 
| 
       239 
     | 
    
         
            -
                  end
         
     | 
| 
       240 
     | 
    
         
            -
                end
         
     | 
| 
       241 
196 
     | 
    
         
             
              end
         
     | 
| 
       242 
197 
     | 
    
         
             
            end
         
     |