#
# Copyright:: Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require_relative "base"
require_relative "../../dist"
module ChefCLI
module Command
module GeneratorCommands
# ## CookbookFile
# chef generate cookbook path/to/basename --generator-cookbook=path/to/generator
#
# Generates a basic cookbook directory structure. Most file types are
# omitted, the user is expected to add additional files as needed using
# the relevant generators.
class Cookbook < Base
banner "Usage: #{ChefCLI::Dist::EXEC} generate cookbook NAME [options]"
attr_reader :errors
attr_reader :cookbook_name_or_path
option :berks,
short: "-b",
long: "--berks",
description: "Generate cookbooks using Berkshelf dependency resolution.",
boolean: true,
default: nil
option :kitchen,
long: "--kitchen CONFIGURATION",
description: "Generate cookbooks with a specific Test Kitchen configuration (dokken|vagrant) - defaults to vagrant",
default: "vagrant"
option :policy,
short: "-P",
long: "--policy",
description: "Generate a cookbook using Policyfile dependency resolution (default).",
boolean: true,
default: nil
option :specs,
short: "-s",
long: "--specs",
description: "Generate a cookbook with sample ChefSpec specs",
boolean: true,
default: nil
option :workflow,
short: "-w",
long: "--workflow",
description: "DEPRECATED: Generate a cookbook with a full #{ChefCLI::Dist::WORKFLOW} build cookbook.",
boolean: true,
default: false
option :verbose,
short: "-V",
long: "--verbose",
description: "Show detailed output from the generator",
boolean: true,
default: false
option :yaml,
short: "-y",
long: "--yaml",
description: "Generate a cookbook with YAML Recipe configuration file as the default.",
boolean: true,
default: nil
option :pipeline,
long: "--pipeline PIPELINE",
description: "Use PIPELINE to set target branch to something other than master for the #{ChefCLI::Dist::WORKFLOW} build_cookbook",
default: "master"
options.merge!(SharedGeneratorOptions.options)
def initialize(params)
@params_valid = true
@cookbook_name = nil
@policy_mode = true
@verbose = false
@specs = false
super
end
def run
read_and_validate_params
if params_valid?
setup_context
msg("Generating cookbook #{cookbook_name}")
chef_runner.converge
msg("")
emit_post_create_message
0
else
err(opt_parser)
1
end
rescue ChefCLI::ChefRunnerError => e
err("ERROR: #{e}")
1
end
def emit_post_create_message
default_recipe_file = yaml ? "default.yml" : "default.rb"
if have_delivery_config?
msg("Your cookbook is ready. To setup the pipeline, type `cd #{cookbook_name_or_path}`, then run `delivery init`")
else
msg("Your cookbook is ready. Type `cd #{cookbook_name_or_path}` to enter it.")
msg("\nThere are several commands you can run to get started locally developing and testing your cookbook.")
msg("Type `delivery local --help` to see a full list of local testing commands.")
msg("\nWhy not start by writing an InSpec test? Tests for the default recipe are stored at:\n")
msg("test/integration/default/default_test.rb")
msg("\nIf you'd prefer to dive right in, the default recipe can be found at:")
msg("\nrecipes/#{default_recipe_file}\n")
end
end
def setup_context
super
Generator.add_attr_to_context(:skip_git_init, cookbook_path_in_git_repo?)
Generator.add_attr_to_context(:cookbook_root, cookbook_root)
Generator.add_attr_to_context(:cookbook_name, cookbook_name)
Generator.add_attr_to_context(:recipe_name, recipe_name)
Generator.add_attr_to_context(:include_chef_repo_source, false)
Generator.add_attr_to_context(:policy_name, policy_name)
Generator.add_attr_to_context(:policy_run_list, policy_run_list)
Generator.add_attr_to_context(:policy_local_cookbook, ".")
Generator.add_attr_to_context(:enable_workflow, enable_workflow?)
Generator.add_attr_to_context(:workflow_project_dir, cookbook_full_path)
Generator.add_attr_to_context(:build_cookbook_parent_is_cookbook, true)
Generator.add_attr_to_context(:workflow_project_git_initialized, have_git? && !cookbook_path_in_git_repo?)
Generator.add_attr_to_context(:verbose, verbose?)
Generator.add_attr_to_context(:specs, specs?)
Generator.add_attr_to_context(:use_policyfile, policy_mode?)
Generator.add_attr_to_context(:pipeline, pipeline)
Generator.add_attr_to_context(:kitchen, kitchen)
Generator.add_attr_to_context(:vscode_dir, create_vscode_dir?)
Generator.add_attr_to_context(:yaml, yaml)
end
def kitchen
config[:kitchen]
end
def pipeline
config[:pipeline]
end
def yaml
config[:yaml]
end
def policy_name
cookbook_name
end
def policy_run_list
"#{cookbook_name}::#{recipe_name}"
end
def recipe
"cookbook"
end
def recipe_name
"default"
end
def cookbook_name
File.basename(cookbook_full_path)
end
def cookbook_root
File.dirname(cookbook_full_path)
end
def cookbook_full_path
File.expand_path(cookbook_name_or_path, Dir.pwd)
end
def policy_mode?
@policy_mode
end
def enable_workflow?
config[:workflow]
end
def verbose?
@verbose
end
def specs?
@specs
end
#
# Is there a .delivery/cli.toml in the current dir or any of the parent dirs
#
# @return [Boolean]
#
def have_delivery_config?
# delivery-cli's logic is to look recursively upward for
# .delivery/cli.toml starting from pwd:
# https://github.com/chef/delivery-cli/blob/22cbef3987ebd0aee98405b7e161a100edc87e49/src/delivery/config/mod.rs#L225-L247
Pathname.pwd.ascend.any? { |path| path.join(".delivery/cli.toml").exist? }
end
def read_and_validate_params
arguments = parse_options(params)
@cookbook_name_or_path = arguments[0]
if !@cookbook_name_or_path
@params_valid = false
elsif File.basename(@cookbook_name_or_path).include?("-")
msg("Hyphens are discouraged in cookbook names as they may cause problems with custom resources. See https://docs.chef.io/workstation/ctl_chef/#chef-generate-cookbook for more information.")
end
if config[:berks] && config[:policy]
err("Berkshelf and Policyfiles are mutually exclusive. Please specify only one.")
@params_valid = false
end
if config[:berks]
@policy_mode = false
end
if config[:verbose]
@verbose = true
end
if config[:specs]
@specs = true
end
true
end
def params_valid?
@params_valid
end
def cookbook_path_in_git_repo?
Pathname.new(cookbook_full_path).ascend do |dir|
return true if File.directory?(File.join(dir.to_s, ".git"))
end
false
end
def create_vscode_dir?
::File.exist?("/Applications/Visual Studio Code.app") || ::File.exist?("#{ENV["APPDATA"]}\\Code")
end
end
end
end
end