# # Author:: Daniel DeLeo () # Author:: Tyler Cloke () # Copyright:: Copyright 2008-2016, 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. # # EX: # deploy "/my/deploy/dir" do # repo "git@github.com/whoami/project" # revision "abc123" # or "HEAD" or "TAG_for_1.0" or (subversion) "1234" # user "deploy_ninja" # enable_submodules true # migrate true # migration_command "rake db:migrate" # environment "RAILS_ENV" => "production", "OTHER_ENV" => "foo" # shallow_clone true # depth 1 # action :deploy # or :rollback # restart_command "touch tmp/restart.txt" # git_ssh_wrapper "wrap-ssh4git.sh" # scm_provider Chef::Provider::Git # is the default, for svn: Chef::Provider::Subversion # svn_username "whoami" # svn_password "supersecret" # end require "chef/resource/scm" class Chef class Resource # Deploy: Deploy apps from a source control repository. # # Callbacks: # Callbacks can be a block or a string. If given a block, the code # is evaluated as an embedded recipe, and run at the specified # point in the deploy process. If given a string, the string is taken as # a path to a callback file/recipe. Paths are evaluated relative to the # release directory. Callback files can contain chef code (resources, etc.) # class Deploy < Chef::Resource identity_attr :repository state_attrs :deploy_to, :revision default_action :deploy allowed_actions :force_deploy, :deploy, :rollback def initialize(name, run_context = nil) super @deploy_to = name @environment = nil @repository_cache = "cached-copy" @copy_exclude = [] @purge_before_symlink = %w{log tmp/pids public/system} @create_dirs_before_symlink = %w{tmp public config} @symlink_before_migrate = { "config/database.yml" => "config/database.yml" } @symlinks = { "system" => "public/system", "pids" => "tmp/pids", "log" => "log" } @revision = "HEAD" @migrate = false @rollback_on_error = false @remote = "origin" @enable_submodules = false @shallow_clone = false @depth = nil @scm_provider = Chef::Provider::Git @svn_force_export = false @additional_remotes = Hash[] @keep_releases = 5 @enable_checkout = true @checkout_branch = "deploy" end # This resource is deprecated. def after_created Chef.deprecated(:deploy_resource, "The #{resource_name} resource (#{source_line}) is deprecated and will be removed from Chef core in 14.0 (April 2018). " \ "A migration cookbook will be available in the future, see the deprecation documentation for more information.") end # where the checked out/cloned code goes def destination @destination ||= shared_path + "/#{@repository_cache}" end # where shared stuff goes, i.e., logs, tmp, etc. goes here def shared_path @shared_path ||= @deploy_to + "/shared" end # where the deployed version of your code goes def current_path @current_path ||= @deploy_to + "/current" end def depth(arg = @shallow_clone ? 5 : nil) set_or_return( :depth, arg, :kind_of => [ Integer ] ) end # note: deploy_to is your application "meta-root." def deploy_to(arg = nil) set_or_return( :deploy_to, arg, :kind_of => [ String ] ) end def repo(arg = nil) set_or_return( :repo, arg, :kind_of => [ String ] ) end alias :repository :repo def remote(arg = nil) set_or_return( :remote, arg, :kind_of => [ String ] ) end def role(arg = nil) set_or_return( :role, arg, :kind_of => [ String ] ) end def restart_command(arg = nil, &block) arg ||= block set_or_return( :restart_command, arg, :kind_of => [ String, Proc ] ) end alias :restart :restart_command def migrate(arg = nil) set_or_return( :migrate, arg, :kind_of => [ TrueClass, FalseClass ] ) end def migration_command(arg = nil) set_or_return( :migration_command, arg, :kind_of => [ String ] ) end def rollback_on_error(arg = nil) set_or_return( :rollback_on_error, arg, :kind_of => [ TrueClass, FalseClass ] ) end def user(arg = nil) set_or_return( :user, arg, :kind_of => [ String ] ) end def group(arg = nil) set_or_return( :group, arg, :kind_of => [ String ] ) end def enable_submodules(arg = nil) set_or_return( :enable_submodules, arg, :kind_of => [ TrueClass, FalseClass ] ) end def shallow_clone(arg = nil) set_or_return( :shallow_clone, arg, :kind_of => [ TrueClass, FalseClass ] ) end def repository_cache(arg = nil) set_or_return( :repository_cache, arg, :kind_of => [ String ] ) end def copy_exclude(arg = nil) set_or_return( :copy_exclude, arg, :kind_of => [ String ] ) end def revision(arg = nil) set_or_return( :revision, arg, :kind_of => [ String ] ) end alias :branch :revision def git_ssh_wrapper(arg = nil) set_or_return( :git_ssh_wrapper, arg, :kind_of => [ String ] ) end alias :ssh_wrapper :git_ssh_wrapper def svn_username(arg = nil) set_or_return( :svn_username, arg, :kind_of => [ String ] ) end def svn_password(arg = nil) set_or_return( :svn_password, arg, :kind_of => [ String ] ) end def svn_arguments(arg = nil) set_or_return( :svn_arguments, arg, :kind_of => [ String ] ) end def svn_info_args(arg = nil) set_or_return( :svn_arguments, arg, :kind_of => [ String ]) end def scm_provider(arg = nil) klass = if arg.kind_of?(String) || arg.kind_of?(Symbol) lookup_provider_constant(arg) else arg end set_or_return( :scm_provider, klass, :kind_of => [ Class ] ) end # This is to support "provider :revision" without deprecation warnings. # Do NOT copy this. def self.provider_base Chef::Provider::Deploy end def svn_force_export(arg = nil) set_or_return( :svn_force_export, arg, :kind_of => [ TrueClass, FalseClass ] ) end def environment(arg = nil) if arg.is_a?(String) Chef::Log.debug "Setting RAILS_ENV, RACK_ENV, and MERB_ENV to `#{arg}'" Chef::Log.warn "[DEPRECATED] please modify your deploy recipe or attributes to set the environment using a hash" arg = { "RAILS_ENV" => arg, "MERB_ENV" => arg, "RACK_ENV" => arg } end set_or_return( :environment, arg, :kind_of => [ Hash ] ) end # The number of old release directories to keep around after cleanup def keep_releases(arg = nil) [set_or_return( :keep_releases, arg, :kind_of => [ Integer ]), 1].max end # An array of paths, relative to your app's root, to be purged from a # SCM clone/checkout before symlinking. Use this to get rid of files and # directories you want to be shared between releases. # Default: ["log", "tmp/pids", "public/system"] def purge_before_symlink(arg = nil) set_or_return( :purge_before_symlink, arg, :kind_of => Array ) end # An array of paths, relative to your app's root, where you expect dirs to # exist before symlinking. This runs after #purge_before_symlink, so you # can use this to recreate dirs that you had previously purged. # For example, if you plan to use a shared directory for pids, and you # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp, # then specify tmp here so that the tmp directory will exist when you # symlink the pids directory in to the current release. # Default: ["tmp", "public", "config"] def create_dirs_before_symlink(arg = nil) set_or_return( :create_dirs_before_symlink, arg, :kind_of => Array ) end # A Hash of shared/dir/path => release/dir/path. This attribute determines # which files and dirs in the shared directory get symlinked to the current # release directory, and where they go. If you have a directory # $shared/pids that you would like to symlink as $current_release/tmp/pids # you specify it as "pids" => "tmp/pids" # Default {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"} def symlinks(arg = nil) set_or_return( :symlinks, arg, :kind_of => Hash ) end # A Hash of shared/dir/path => release/dir/path. This attribute determines # which files in the shared directory get symlinked to the current release # directory and where they go. Unlike map_shared_files, these are symlinked # *before* any migration is run. # For a rails/merb app, this is used to link in a known good database.yml # (with the production db password) before running migrate. # Default {"config/database.yml" => "config/database.yml"} def symlink_before_migrate(arg = nil) set_or_return( :symlink_before_migrate, arg, :kind_of => Hash ) end # Callback fires before migration is run. def before_migrate(arg = nil, &block) arg ||= block set_or_return(:before_migrate, arg, :kind_of => [Proc, String]) end # Callback fires before symlinking def before_symlink(arg = nil, &block) arg ||= block set_or_return(:before_symlink, arg, :kind_of => [Proc, String]) end # Callback fires before restart def before_restart(arg = nil, &block) arg ||= block set_or_return(:before_restart, arg, :kind_of => [Proc, String]) end # Callback fires after restart def after_restart(arg = nil, &block) arg ||= block set_or_return(:after_restart, arg, :kind_of => [Proc, String]) end def additional_remotes(arg = nil) set_or_return( :additional_remotes, arg, :kind_of => Hash ) end def enable_checkout(arg = nil) set_or_return( :enable_checkout, arg, :kind_of => [TrueClass, FalseClass] ) end def checkout_branch(arg = nil) set_or_return( :checkout_branch, arg, :kind_of => String ) end # FIXME The Deploy resource may be passed to an SCM provider as its # resource. The SCM provider knows that SCM resources can specify a # timeout for SCM operations. The deploy resource must therefore support # a timeout method, but the timeout it describes is for SCM operations, # not the overall deployment. This is potentially confusing. def timeout(arg = nil) set_or_return( :timeout, arg, :kind_of => Integer ) end end end end