Capistranoで自動デプロイ

# Capistrano

## 前提条件

* config 2.0.0
* dotenv-rails 2.7.5
* Capistrano 3.11.2
* slackistrano >= 4.0.1
* pushion passenger( Nginx 

## セットアップ

* Capistranoのインストール

```sh:Terminal
$ bundle exec cap install STAGES=staging,production
bundler: command not found: cap
Install missing gem executables with `bundle install`
```

あら。まあそりゃそうです笑

### command not found: capとなった場合

- この場合、capistranoのGEMがインストールされていない為、GEMインストールを行う

- Gemfileを開き、編集

```sh:Terminal

# Gemfileの編集
$ vi Gemfile
group :staging, :production do
  # Use Capistrano for deployment
  gem 'capistrano',                 '3.11.2'
  gem 'capistrano-rails',           '1.4.0'
  gem 'capistrano-rbenv',           '2.1.4 '
  gem 'capistrano-passenger',       '0.2.0'
  # 今回はSlack通知も行うため、便宜上載せています
  gem 'slackistrano', '>=4.0.1'
end
```

- bundle install

```sh:Terminal

$ bundle _1.17.2_ install --path vendor/bundle
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
・・・
Fetching capistrano 3.11.2
Installing capistrano 3.11.2
Fetching capistrano-bundler 1.6.0
Installing capistrano-bundler 1.6.0
Fetching capistrano-passenger 0.2.0
Installing capistrano-passenger 0.2.0
Fetching capistrano-rails 1.4.0
Installing capistrano-rails 1.4.0
Fetching capistrano-rbenv 2.1.4
Installing capistrano-rbenv 2.1.4
・・・
Fetching slackistrano 4.0.1
Installing slackistrano 4.0.1
・・・
Bundle complete! 38 Gemfile dependencies, 148 gems now installed.
Bundled gems are installed into `./vendor/bundle`
Post-install message from capistrano-passenger:
==== Release notes for capistrano-passenger ====
passenger once had only one way to restart: `touch tmp/restart.txt`
Beginning with passenger v4.0.33, a new way was introduced: `passenger-config restart-app`

The new way to restart was not initially practical for everyone,
since for versions of passenger prior to v5.0.10,
it required your deployment user to have sudo access for some server configurations.

capistrano-passenger gives you the flexibility to choose your restart approach, or to rely on reasonable defaults.

If you want to restart using `touch tmp/restart.txt`, add this to your config/deploy.rb:

    set :passenger_restart_with_touch, true

If you want to restart using `passenger-config restart-app`, add this to your config/deploy.rb:

    set :passenger_restart_with_touch, false # Note that `nil` is NOT the same as `false` here

If you don't set `:passenger_restart_with_touch`, capistrano-passenger will check what version of passenger you are running
and use `passenger-config restart-app` if it is available in that version.

If you are running passenger in standalone mode, it is possible for you to put passenger in your
Gemfile and rely on capistrano-bundler to install it with the rest of your bundle.
If you are installing passenger during your deployment AND you want to restart using `passenger-config restart-app`,
you need to set `:passenger_in_gemfile` to `true` in your `config/deploy.rb`.
================================================
```

- CapistranoとCapifyアプリケーションをインストールする

```sh:Terminal

$ bundle exec cap install STAGES=staging,production
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified
```

階層構造は以下となる

```text:Tree-map
[rails app root]
├─ Capfile
├─ config
│ ├─ deploy
│ │ ├─production.rb
│ │ └─staging.rb
│ └─deploy.rb
└─ lib
  └─capistrano
    └─tasks
  └─tasks
```

capistranoをインストールしたので、capistrano/setup と capistrano/deploy の準備は一旦はこれでできました

## [capistrano/deploy タスク](https://github.com/capistrano/capistrano/blob/master/lib/capistrano/tasks/deploy.rake)

* デプロイを自動化する、capistranoのタスクです

### Deployタスクの一覧

* [framework.rake](https://github.com/capistrano/capistrano/blob/v3.11.2/lib/capistrano/tasks/framework.rake)

| 実行順 | タスク名 | 意味 |
|:------|:-------|:-----|
| 1 | deploy | Deploy実行 |
| 2 | deploy:starting | Deploy実行開始、サーバー待機中 |
| 2.1 | deploy:check | - |
| 2.1.1 | #{scm}:check | - |
| 2.1.2 | deploy:check:directories | - |
| 2.1.3 | deploy:check:linked_dirs | - |
| 2.1.4 | deploy:check:make_linked_dirs | - |
| 2.1.5 | deploy:check:linked_files | - |
| 2.2 | deploy:set_previous_revision | - |
| 3 | deploy:started | Deploy開始中 |
| 4 | deploy:updating | Deploy実行中 |
| 4.1 | #{scm}:create_release | - |
| 4.2 | deploy:symlink:shared | - |
| 4.2.1 | deploy:symlink:linked_files | - |
| 4.2.2 | deploy:symlink:linked_dirs | - |
| 5 | deploy:updated | 更新完了 |
| 6 | deploy:publishing | デプロイ処理中 |
| 6.1 | deploy:symlink:release | - |
| 7 | deploy:published | デプロイ処理終了 |
| 8 | deploy:finishing | デプロイ終了中、サーバークリーンアップ中 |
| 8.1 | deploy:cleanup | - |
| 9 | deploy:finished | 処理終了 |
| 9.1 | deploy:log_revision | - |

| 実行順 | タスク名 | 意味 |
|:------|:-------|:-----|
| 1 | deploy:rollback | 前回リリースバージョンにロールバック |
| 2 | deploy:starting | Deploy実行開始、サーバー待機中 |
| 3 | deploy:started | Deploy開始中 |
| 4 | deploy:reverting | 前回リリースバージョンにrevert完了 |
| 5 | deploy:reverted | 前回リリースバージョンにrevert |
| 6 | deploy:publishing | デプロイ処理中 |
| 7 | deploy:published | デプロイ処理終了 |
| 8 | deploy:finishing_rollback | ロールバック完了 |
| 9 | deploy:finished | 処理終了 |

| 実行順 | タスク名 | 意味 |
|:------|:-------|:-----|
| 1 | install | Capistranoインストール<br>（凡例：cap install STAGES=staging,production） |

* 参考になった技術まとめ
    * [Capistrano3のデプロイフレームワークの使い方](https://qiita.com/yuku_t/items/01c0ec4389db143e27f5)

### プラグインによるフローの追加

#### [capistrano/rails/assets](https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/assets.rake)

* Assetsのプリコンパイルを行う

##### インストール

```ruby:Capfile
require capistrano/rails/assets
```

| 実行順 | タスク名 | 意味 |
|:------|:-------|:-----|
| 5 | deploy:updated | 更新完了 |
| 5.1 | deploy:compile_assets | 追加される |
| 5.1.1 | deploy:assets:precompile | 追加される |
| 5.1.2 | deploy:assets:backup_manifest | 追加される |
| 5.2 | deploy:normalize_assets | 追加される |


#### [capistrano/rails/migrations](https://github.com/capistrano/rails/blob/master/lib/capistrano/tasks/migrations.rake)

* DBMigrationを行う

##### インストール

```ruby:Capfile
require "capistrano/rails/migrations"
```

| 実行順 | タスク名 | 意味 |
|:------|:-------|:-----|
| 5 | deploy:updated | 更新完了 |
| 5.1 | deploy:migrate | 追加される |



#### [capistrano/npm](https://github.com/capistrano/npm/blob/master/lib/capistrano/tasks/npm.rake)

* npm installを行う

##### インストール

```ruby:Capfile
require "capistrano/rails/migrations"
```

| 実行順 | タスク名 | 意味 |
|:------|:-------|:-----|
| 4 | deploy:updating | Deploy実行中 |
| 4.1 | #{scm}:create_release | - |
| 4.2 | deploy:symlink:shared | - |
| 4.2.1 | deploy:symlink:linked_files | - |
| 4.2.2 | deploy:symlink:linked_dirs | - |
| 5 | npm:install | 追加される |


### 前提条件

* ssh-agentを使用


### セットアップ手順

- `config/settings/staging.yml` を作成
- `config/environments/staging.rb` を作成
- `Capfile` を編集する

```ruby:Capfile
# Load DSL and set up stages
require "capistrano/setup"

# Include default deployment tasks
require "capistrano/deploy"

# Load the SCM plugin appropriate to your project:
#
# require "capistrano/scm/hg"
# install_plugin Capistrano::SCM::Hg
# or
# require "capistrano/scm/svn"
# install_plugin Capistrano::SCM::Svn
# or
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

# Include tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
#   https://github.com/capistrano/rvm
#   https://github.com/capistrano/rbenv
#   https://github.com/capistrano/chruby
#   https://github.com/capistrano/bundler
#   https://github.com/capistrano/rails
#   https://github.com/capistrano/passenger
#
# require "capistrano/rvm"
# require "capistrano/rbenv"
# require "capistrano/chruby"
# require "capistrano/bundler"
# require "capistrano/rails/assets"
# require "capistrano/rails/migrations"
# require "capistrano/passenger"
# if you used rbenv
require "capistrano/rbenv"

require 'capistrano/rails'

require 'capistrano/bundler'

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
```

- `deploy.rb` を編集する

```ruby:config/deploy.rb

# config valid for current version and patch releases of Capistrano
lock "~> 3.11.2"

set :application, "%app name%"
set :repo_url, "git@github.com:****/*****.git"

# Default branch is :master
# masterで良いので、特に変更は不要
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
# bundle exec cap %env% deploy BRANCH=feature/hogehoge
# set :branch, ENV['BRANCH'] || 'master'

# Default deploy_to directory is /var/www/my_app_name
# set :deploy_to, "/var/www/my_app_name"
set :deploy_to, "/var/www/#{fetch(:application)}"

# Default value for :format is :airbrussh.
# set :format, :airbrussh
# Configure version management tool
set :scm, :git

# You can configure the Airbrussh format using :format_options.
# These are the defaults.
# set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
# Configure log_leve if you output the log file
# set :format, :pretty
# set :log_level, :info

# Default value for :pty is false
# set :pty, true
# sudoを使用する場合はtrue
set :pty, true

# Default value for :linked_files is []
# append :linked_files, "config/database.yml"
# ln -s deploy_to/shared/config/database.yml :deploy_to/current/config/database.yml
set :linked_files, %w{config/database.yml}

# Default value for linked_dirs is []
# append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
# sharedのディレクトリをcurrentにシンボリックリンクする
#set :linked_dirs, %w{bin log tmp/backup tmp/pids tmp/cache tmp/sockets vendor/bundle}
# binはrails consoleが必要なため、残す
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle')
set :rbenv_map_bins, %w{rake gem bundle ruby rails}

# Default value for default_env is {}
# capistrano用bundleするのに必要
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
set :default_env, { path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH" }

# Default value for local_user is ENV['USER']
# set :local_user, -> { `git config user.name`.chomp }

# Default value for keep_releases is 5
# set :keep_releases, 5
# How many keep the component of deployed in the "releases" directory
set :keep_releases, 5

# Uncomment the following to require manually verifying the host key before first deploy.
# set :ssh_options, verify_host_key: :secure
# Configure ssh agent
set :ssh_options, {
  keys: %w(~/.ssh/id_rsa),
  forward_agent: true,
  auth_methods: %w(publickey)
}

# Configure ur Slack's Incoming Webhook
# if you want to be disable deployment notifications to a specific stage by setting the :slackistrano configuration variable to false instead of actual settings.
set :slackistrano, false
#set :slackistrano, {
#  klass:   Slackistrano::CustomMessaging,
#  channel: '#chatops-#{fetch(:application)}-deploy',
#  webhook: 'https://hooks.slack.com/services/*/*/*'
#}

# Have you installed rbenv in the global area? Or was it installed in the user local area?
# :system or :user
set :rbenv_type, :user
# rubyのversion
set :rbenv_ruby, '2.6.5'
set :rbenv_path, '/usr/local/rbenv'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all # default value

# Configure 
# rollback / deployを判定する為、before startingのcallbackで判定を取得する
# 参考：https://github.com/capistrano/capistrano/blob/v3.11.2/lib/capistrano/tasks/framework.rake#L57
before 'deploy:starting', 'slack:deploy:updating'

# https://github.com/capistrano/capistrano/blob/v3.11.2/lib/capistrano/dsl/task_enhancements.rb#L51
# https://www.rubydoc.info/github/capistrano/capistrano/Capistrano/TaskEnhancements
# 既存処理をOverride
module Capistrano
  module TaskEnhancements
    def exit_deploy_because_of_exception(ex)
      set :failed_exception, ex
      super ex
    end
  end
end

# Edited deploy task
namespace :deploy do

  desc 'git confirm'
  task :git_confirm do
    on release_roles :all do

      current = `git rev-parse --abbrev-ref HEAD`.chomp
      correct = fetch(:branch) || 'master'
      unless current == correct
        error "git: current branch is not '#{correct}' but '#{current}'."
        exit 1
      end
      # gitにdiffがないかどうかのチェック
      git_diff = `git diff`
      unless git_diff.empty?
        error "git: current source has any diff. \n#{git_diff}"
        exit 1
      end
    end
  end

  # 上記linked_filesで使用するファイルをアップロードするタスク
  desc 'Upload database.yml'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/database.yml', "#{shared_path}/config/database.yml")
    end
  end

  desc 'Restart application'
  task :restart do
    # on roles(:app) do
    #   invoke 'unicorn:restart'
    # end
    on roles(:app), in: :sequence, wait: 5 do
      # Your restart mechanism here, for example:
      execute :touch, release_path.join('tmp/restart.txt')
    end
  end

  desc '.env publishing'
  task :env_publising do
    on roles(:app) do |host|
      if test "[ -f #{fetch(:deploy_to)}/.env ]"
        execute "cp -a #{fetch(:deploy_to)}/.env #{fetch(:release_path)}/.env"
      end
    end
  end
end

before 'deploy', 'deploy:git_confirm'
before 'deploy:starting', 'deploy:upload'
  # Enhance dotenv
after  'deploy:published', 'deploy:env_publising'
after  'deploy:published', 'deploy:restart'
```

1. 各環境のデプロイ用ファイルの編集

```ruby:config/deploy/stating.rb
# server-based syntax
# ======================
# Defines a single server with a list of roles and multiple properties.
# You can define all roles on a single server, or split them:

# server "example.com", user: "deploy", roles: %w{app db web}, my_property: :my_value
# server "example.com", user: "deploy", roles: %w{app web}, other_property: :other_value
# server "db.example.com", user: "deploy", roles: %w{db}



# role-based syntax
# ==================

# Defines a role with one or multiple servers. The primary server in each
# group is considered to be the first unless any hosts have the primary
# property set. Specify the username and a domain or IP for the server.
# Don't use `:all`, it's a meta role.

# role :app, %w{deploy@example.com}, my_property: :my_value
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
# role :db,  %w{deploy@example.com}



# Configuration
# =============
# You can set any configuration variable like in config/deploy.rb
# These variables are then only loaded and set in this stage.
# For available Capistrano configuration variables see the documentation page.
# http://capistranorb.com/documentation/getting-started/configuration/







# Custom SSH Options
# ==================
# You may pass any option but keep in mind that net/ssh understands a
# limited set of options, consult the Net::SSH documentation.
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
#
# Global options
# --------------
#  set :ssh_options, {
#    keys: %w(/home/rlisowski/.ssh/id_rsa),
#    forward_agent: false,
#    auth_methods: %w(password)
#  }
#
# The server-based syntax can be used to override options:
# ------------------------------------
# server "example.com",
#   user: "user_name",
#   roles: %w{web app},
#   ssh_options: {
#     user: "user_name", # overrides user setting above
#     keys: %w(/home/user_name/.ssh/id_rsa),
#     forward_agent: false,
#     auth_methods: %w(publickey password)
#     # password: "please use keys"
#   }

set :stage, :staging
set :branch, ENV['GIT_BRANCH'] || `git rev-parse --abbrev-ref HEAD`.strip
set :rails_env, "staging"

# Only setup application server role
# role :app, %w{USER_NAME@IP_ADDRESS}

# capistrano_sample_app_v1
server "%hostname%", user: `whoami`.strip, roles: %w{app}
```

```ruby:config/deploy/production.rb
# server-based syntax
# ======================
# Defines a single server with a list of roles and multiple properties.
# You can define all roles on a single server, or split them:

# server "example.com", user: "deploy", roles: %w{app db web}, my_property: :my_value
# server "example.com", user: "deploy", roles: %w{app web}, other_property: :other_value
# server "db.example.com", user: "deploy", roles: %w{db}



# role-based syntax
# ==================

# Defines a role with one or multiple servers. The primary server in each
# group is considered to be the first unless any hosts have the primary
# property set. Specify the username and a domain or IP for the server.
# Don't use `:all`, it's a meta role.

# role :app, %w{deploy@example.com}, my_property: :my_value
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
# role :db,  %w{deploy@example.com}



# Configuration
# =============
# You can set any configuration variable like in config/deploy.rb
# These variables are then only loaded and set in this stage.
# For available Capistrano configuration variables see the documentation page.
# http://capistranorb.com/documentation/getting-started/configuration/
# Feel free to add new variables to customise your setup.



# Custom SSH Options
# ==================
# You may pass any option but keep in mind that net/ssh understands a
# limited set of options, consult the Net::SSH documentation.
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
#
# Global options
# --------------
#  set :ssh_options, {
#    keys: %w(/home/rlisowski/.ssh/id_rsa),
#    forward_agent: false,
#    auth_methods: %w(password)
#  }
#
# The server-based syntax can be used to override options:
# ------------------------------------
# server "example.com",
#   user: "user_name",
#   roles: %w{web app},
#   ssh_options: {
#     user: "user_name", # overrides user setting above
#     keys: %w(/home/user_name/.ssh/id_rsa),
#     forward_agent: false,
#     auth_methods: %w(publickey password)
#     # password: "please use keys"
#   }

set :stage, :production
set :branch, "master"
set :rails_env, "production"

role :app, %w{USER_NAME@IP_ADDRESS}
```

- Slackistranoの設定

```ruby:lib/slackistrano/custom_messaging.rb
if defined?(Slackistrano::Messaging)
   module Slackistrano
     class CustomMessaging < Messaging::Base

      # Suppress starting message.
      def payload_for_updating

        attachments = []
        # rollback / deployを判定する為、before startingのcallbackで判定を取得する
        title = fetch(:deploying, false) ? 'deploying' : 'rollback now'
        color = fetch(:deploying, false) ? 'warning' : 'danger'

        # https://github.com/phallstrom/slackistrano
        attachments = attachments_base(title: title, color: color)

        # git log
        attachments << {
          color:  color,
          title:  "git log",
          fields: git_last_commits_fields
        }

        return {attachments: attachments}
      end

      # Suppress updated message.
      def payload_for_updated
        attachments = [] + attachments_base(title: 'Deploy complete', color: 'good')
        return {attachments: attachments}
      end

      # Supperss reverting message.
      def payload_for_reverting
        # Delegate payload_for_updating
      end

      # Suppress reverted message.
      def payload_for_reverted
        attachments = [] + attachments_base(title: 'Revert complete', color: 'good')
        return {attachments: attachments}
      end

      # Suppress failred message.
      def payload_for_failed
        attachments = [] + attachments_base(title: 'Deploy failred', color: 'good')

        # エラー内容を取得
        exception = fetch(:failed_exception)
        trace_message = exception.inspect + "\n" + exception.backtrace.join("\n")

        # エラー内容
        attachments << {
          color: 'danger',
          title: "エラー内容",
          fields: [{
            value: trace_message
          }]
        }
        return {attachments: attachments}
      end

      private
        def attachments_base(title = "", color = "")
          {
            color: color,
            title: "#{application} #{title}",
            fields: [
              {
                title: '環境',
                value: stage,
                short: true
              },
              {
                title: 'ブランチ',
                value: branch,
                short: true
              },
              {
                title: 'Capistrano実行者',
                value: deployer,
                short: true
              },
              {
                title: '実行時間',
                value: elapsed_time,
                short: true
              }
            ],
            fallback: super[:text]
          }
        end

        def deployer
          `whoami`.strip
        end
    end
  end
end
```

変更の構成は以下の通り

```sh:Terminal
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  modified:   Gemfile
  modified:   Gemfile.lock
  modified:   config/environments/development.rb
  modified:   config/environments/staging.rb
  modified:   config/environments/production.rb

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  Capfile
  config/deploy.rb
  config/deploy/
  lib/slackistrano/
```

ADD

```sh:Terminal
$ git add Gemfile*
$ git add config/environments
$ git add Capfile
$ git add config/deploy*
$ git add lib/slackistrano
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  new file:   Capfile
  modified:   Gemfile
  modified:   Gemfile.lock
  new file:   config/deploy.rb
  new file:   config/deploy/production.rb
  new file:   config/deploy/staging.rb
  modified:   config/environments/development.rb
  modified:   config/environments/production.rb
  new file:   lib/slackistrano/custom_messaging.rb

```

コミット＆push

```sh:Terminal
$ git commit -m "Capistranoの設定"
[master 26dbe1b] Capistranoの設定
 9 files changed, 567 insertions(+), 3 deletions(-)
 create mode 100644 Capfile
 create mode 100644 config/deploy.rb
 create mode 100644 config/deploy/production.rb
 create mode 100644 config/deploy/staging.rb
 create mode 100644 lib/slackistrano/custom_messaging.rb

$ git push --set-upstream origin master
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 8 threads
Compressing objects: 100% (15/15), done.
Writing objects: 100% (16/16), 9.37 KiB | 3.12 MiB/s, done.
Total 16 (delta 6), reused 0 (delta 0)
remote: Resolving deltas: 100% (6/6), completed with 5 local objects.
To https://github.com/%organization%/%repos name%.git
   908k3a9..8u43k19  master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

```

* 参考になった技術まとめ
  * Configure ssh agent
      * [capistranoでデプロイする時のssh-key周りのTips](https://qiita.com/Takkiii/items/c4f2cfd143e97e061e66)
  * SSH agent
      * [ssh-agentの使い方](https://qiita.com/isaoshimizu/items/84ac5a0b1d42b9d355cf)


## SSH agentの設定

`config/deploy/stating.rb` の `server` で指定したホスト環境にSSH agentをセットアップする

```sh:Terminal
%hostname%]$ ssh-add -l
4096 SHA256:%masking% /home/%user%/.ssh/id_rsa (RSA)
```

詳細は `ssh-agentの使い方` を参照


## [Slackistrano](https://github.com/phallstrom/slackistrano)

* Capistranoの自動デプロイをChatOpsに組み込みたいので、Incoming Webhookに連携します

### セットアップ手順

- `config/deploy.rb` を編集する

```ruby:config/deploy.rb
# Configure ur Slack's Incoming Webhook
# if you want to be disable deployment notifications to a specific stage by setting the :slackistrano configuration variable to false instead of actual settings.
# When you desable slackistrano, comment out the description below.
set :slackistrano, false # falseの場合はslackistranoによるIncoming Webhookは発生しない
# set :slackistrano, { # こちらは発生する設定、上記かこちらのいずれかを記載する
#  klass:   Slackistrano::CustomMessaging,
#  channel: '#chatops-#{fetch(:application)}-deploy',
#  webhook: 'https://hooks.slack.com/services/*/*/*'
#}
```

- `Capfile` を編集する

```ruby:Capfile
・・・省略・・・
require 'capistrano/bundler'

# Require the slackistrano need the deploy.rb
require 'slackistrano/capistrano'
```

### NameError: uninitialized constant Slackistrano::CustomMessaging が発生した場合

- この場合、 `Capfile` にrequireでGemがロードされていないのが原因の為、Capfileを編集する

- Capfileを開き、編集（上記セットアップ手順を忘れていることが考えられる）

## デバッグ実行

* 設定が一通り終わったら、次はデバッグ実行でチェックしてみます

* `--dry-run` オプションで実行せずにデプロイをシミュレーションします
* `trace` オプションでデプロイ実行中の例外スロー（スタックトレース？）をします

```sh:Terminal
$ bundle exec cap staging deploy --dry-run --trace
** Invoke staging (first_time)
** Execute staging
** Invoke load:defaults (first_time)
** Execute load:defaults
[Deprecation Notice] `set :scm, :git` is deprecated.
To ensure your project is compatible with future versions of Capistrano,
remove the :scm setting and instead add these lines to your Capfile after
`require "capistrano/deploy"`:
    require "capistrano/scm/git"
    install_plugin Capistrano::SCM::Git
** Invoke rbenv:validate (first_time)
** Execute rbenv:validate
** Invoke rbenv:map_bins (first_time)
** Execute rbenv:map_bins
** Invoke bundler:map_bins (first_time)
** Execute bundler:map_bins
** Invoke deploy:set_rails_env (first_time)
** Execute deploy:set_rails_env
** Invoke deploy:set_linked_dirs (first_time)
** Execute deploy:set_linked_dirs
** Invoke deploy:set_rails_env 
** Invoke deploy (first_time)
** Invoke deploy:git_confirm (first_time)
** Execute deploy:git_confirm
00:00 deploy:git_confirm
** Execute deploy
** Invoke deploy:starting (first_time)
** Invoke slack:deploy:starting (first_time)
** Execute slack:deploy:starting
** Invoke slack:deploy:updating (first_time)
** Execute slack:deploy:updating
** Invoke deploy:upload (first_time)
** Execute deploy:upload
00:00 deploy:upload
      01 mkdir -p /var/www/capistrano_sample_app_v1/shared/config
      02 config/database.yml /var/www/capistrano_sample_app_v1/shared/config/database.yml
** Execute deploy:starting
** Invoke deploy:check (first_time)
** Invoke git:check (first_time)
** Invoke git:wrapper (first_time)
** Execute git:wrapper
00:00 git:wrapper
      01 mkdir -p /tmp
      02 #<StringIO:0x00000000025f9838> /tmp/git-ssh-capistrano_sample_app_v1-staging-webmaster-patche.sh
      03 chmod 700 /tmp/git-ssh-capistrano_sample_app_v1-staging-webmaster-patche.sh
      04 #<StringIO:0x0000000002617720> /tmp/git-ssh-capistrano_sample_app_v1-staging-webmaster-patche.sh
** Execute git:check
00:00 git:check
      01 git ls-remote git@github.com:webmaster-patche/capistrano_sample_app_v1.git HEAD
** Execute deploy:check
** Invoke deploy:check:directories (first_time)
** Execute deploy:check:directories
00:00 deploy:check:directories
      01 mkdir -p /var/www/capistrano_sample_app_v1/shared /var/www/capistrano_sample_app_v1/releases
** Invoke deploy:check:linked_dirs (first_time)
** Execute deploy:check:linked_dirs
00:00 deploy:check:linked_dirs
      01 mkdir -p /var/www/capistrano_sample_app_v1/shared/bin /var/www/capistrano_sample_app_v1/shared/log /var/www/capistrano_sample_app_v1/shared/tmp/backup /var/www/capistrano_sample_app_v1/shared/tmp/pids /var/www/capistrano_sample_app_v1/shared/tmp/cache /var/www/capistrano_sample_app_v1/shared/t…
** Invoke deploy:check:make_linked_dirs (first_time)
** Execute deploy:check:make_linked_dirs
00:00 deploy:check:make_linked_dirs
      01 mkdir -p /var/www/capistrano_sample_app_v1/shared/config
** Invoke deploy:check:linked_files (first_time)
** Execute deploy:check:linked_files
** Invoke deploy:set_previous_revision (first_time)
** Execute deploy:set_previous_revision
** Invoke deploy:started (first_time)
** Execute deploy:started
** Invoke deploy:updating (first_time)
** Invoke deploy:new_release_path (first_time)
** Execute deploy:new_release_path
** Invoke git:create_release (first_time)
** Invoke git:update (first_time)
** Invoke git:clone (first_time)
** Invoke git:wrapper 
** Execute git:clone
00:00 git:clone
      The repository mirror is at /var/www/capistrano_sample_app_v1/repo
      The repository mirror is at /var/www/capistrano_sample_app_v1/repo
** Execute git:update
00:00 git:update
      01 git remote set-url origin git@github.com:webmaster-patche/capistrano_sample_app_v1.git
      02 git remote update --prune
** Execute git:create_release
00:00 git:create_release
      01 mkdir -p /var/www/capistrano_sample_app_v1/releases/*******
      02 git archive master | /usr/bin/env tar -x -f - -C /var/www/capistrano_sample_app_v1/releases/*******
** Invoke slack:deploy:updating 
** Execute deploy:updating
** Invoke deploy:set_current_revision (first_time)
** Invoke git:set_current_revision (first_time)
** Execute git:set_current_revision
** Execute deploy:set_current_revision
00:00 deploy:set_current_revision
      01 echo "" > REVISION
** Invoke deploy:symlink:shared (first_time)
** Execute deploy:symlink:shared
** Invoke deploy:symlink:linked_files (first_time)
** Execute deploy:symlink:linked_files
00:00 deploy:symlink:linked_files
      01 mkdir -p /var/www/capistrano_sample_app_v1/releases/*******/config
** Invoke deploy:symlink:linked_dirs (first_time)
** Execute deploy:symlink:linked_dirs
00:00 deploy:symlink:linked_dirs
      01 mkdir -p /var/www/capistrano_sample_app_v1/releases/******* /var/www/capistrano_sample_app_v1/releases/*******/tmp /var/www/capistrano_sample_app_v1/releases/*******/vendor /var/www/capistrano_sample_app_v1/releases/*******/public
** Invoke deploy:updated (first_time)
** Invoke bundler:install (first_time)
** Execute bundler:install
00:00 bundler:install
      The Gemfile's dependencies are satisfied, skipping installation
      The Gemfile's dependencies are satisfied, skipping installation
** Execute deploy:updated
** Invoke deploy:compile_assets (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:compile_assets
** Invoke deploy:assets:precompile (first_time)
** Execute deploy:assets:precompile
** Invoke deploy:assets:backup_manifest (first_time)
** Execute deploy:assets:backup_manifest
** Invoke deploy:cleanup_assets (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:cleanup_assets
** Invoke deploy:normalize_assets (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:normalize_assets
** Invoke deploy:migrate (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:migrate
** Invoke deploy:publishing (first_time)
** Execute deploy:publishing
** Invoke deploy:symlink:release (first_time)
** Execute deploy:symlink:release
00:00 deploy:symlink:release
      01 ln -s /var/www/capistrano_sample_app_v1/releases/******* /var/www/capistrano_sample_app_v1/releases/current
      02 mv /var/www/capistrano_sample_app_v1/releases/current /var/www/capistrano_sample_app_v1
** Invoke deploy:published (first_time)
** Execute deploy:published
** Invoke deploy:finishing (first_time)
** Execute deploy:finishing
** Invoke deploy:cleanup (first_time)
** Execute deploy:cleanup
** Invoke slack:deploy:updated (first_time)
** Execute slack:deploy:updated
** Invoke deploy:finished (first_time)
** Execute deploy:finished
** Invoke deploy:log_revision (first_time)
** Execute deploy:log_revision
00:00 deploy:log_revision
      01 echo "Branch master (at ) deployed as release 20200108015441 by webmaster-patche" >> /var/www/capistrano_sample_app_v1/revisions.log
```

## 実行

* 設定が一通り終わったら、次はデバッグではなく実際に実行でチェックしてみます

* `trace` オプションでデプロイ実行中の例外スロー（スタックトレース？）をします

```sh:Terminal
$ bundle exec cap staging deploy --trace
** Invoke staging (first_time)
** Execute staging
** Invoke load:defaults (first_time)
** Execute load:defaults
[Deprecation Notice] `set :scm, :git` is deprecated.
To ensure your project is compatible with future versions of Capistrano,
remove the :scm setting and instead add these lines to your Capfile after
`require "capistrano/deploy"`:

    require "capistrano/scm/git"
    install_plugin Capistrano::SCM::Git

** Invoke rbenv:validate (first_time)
** Execute rbenv:validate
** Invoke rbenv:map_bins (first_time)
** Execute rbenv:map_bins
** Invoke bundler:map_bins (first_time)
** Execute bundler:map_bins
** Invoke deploy:set_rails_env (first_time)
** Execute deploy:set_rails_env
** Invoke deploy:set_linked_dirs (first_time)
** Execute deploy:set_linked_dirs
** Invoke deploy:set_rails_env 
** Invoke deploy (first_time)
** Invoke deploy:git_confirm (first_time)
** Execute deploy:git_confirm
00:00 deploy:git_confirm
      [git:confirm] Skip git confirm
** Execute deploy
** Invoke deploy:starting (first_time)
** Invoke slack:deploy:starting (first_time)
** Execute slack:deploy:starting
** Invoke slack:deploy:updating (first_time)
** Execute slack:deploy:updating
** Invoke deploy:upload (first_time)
** Execute deploy:upload
00:00 deploy:upload
      Uploading config/database.yml 100.0%
** Execute deploy:starting
** Invoke deploy:check (first_time)
** Invoke git:check (first_time)
** Invoke git:wrapper (first_time)
** Execute git:wrapper
00:00 git:wrapper
      01 mkdir -p /tmp
    ✔ 01 %user%@%hostname% 0.139s
      Uploading /tmp/git-ssh-capistrano_sample_app_v1-staging-%user%.sh 100.0%
      02 chmod 700 /tmp/git-ssh-capistrano_sample_app_v1-staging-%user%.sh
    ✔ 02 %user%@%hostname% 0.142s
** Execute git:check
00:00 git:check
      01 git ls-remote git@github.com:%user%/capistrano_sample_app_v1.git HEAD
      01 78ee082f5b26fcac1ad4aa25410dcc3d1b32d51e HEAD
    ✔ 01 %user%@%hostname% 1.904s
** Execute deploy:check
** Invoke deploy:check:directories (first_time)
** Execute deploy:check:directories
00:02 deploy:check:directories
      01 mkdir -p /var/www/capistrano_sample_app_v1/shared /var/www/capistrano_sample_app_v1/releases
    ✔ 01 %user%@%hostname% 0.097s
** Invoke deploy:check:linked_dirs (first_time)
** Execute deploy:check:linked_dirs
00:02 deploy:check:linked_dirs
      01 mkdir -p /var/www/capistrano_sample_app_v1/shared/log /var/www/capistrano_sample_app_v1/shared/tmp/pids /var/www/capistrano_sample_app_v1/shared/tmp/cache /var/www/capistrano_sample_app_v1/shared/tmp/sockets /var/www/capistrano_sample_app_v1/shared/vendor/bundle /var/www/capistrano_sample_app_v1/shared/public/ass…
    ✔ 01 %user%@%hostname% 0.140s
** Invoke deploy:check:make_linked_dirs (first_time)
** Execute deploy:check:make_linked_dirs
00:02 deploy:check:make_linked_dirs
      01 mkdir -p /var/www/capistrano_sample_app_v1/shared/config
    ✔ 01 %user%@%hostname% 0.140s
** Invoke deploy:check:linked_files (first_time)
** Execute deploy:check:linked_files
** Invoke deploy:set_previous_revision (first_time)
** Execute deploy:set_previous_revision
** Invoke deploy:started (first_time)
** Execute deploy:started
** Invoke deploy:updating (first_time)
** Invoke deploy:new_release_path (first_time)
** Execute deploy:new_release_path
** Invoke git:create_release (first_time)
** Invoke git:update (first_time)
** Invoke git:clone (first_time)
** Invoke git:wrapper 
** Execute git:clone
00:03 git:clone
      The repository mirror is at /var/www/capistrano_sample_app_v1/repo
** Execute git:update
00:03 git:update
      01 git remote set-url origin git@github.com:%user%/capistrano_sample_app_v1.git
    ✔ 01 %user%@%hostname% 0.142s
      02 git remote update --prune
      02 Fetching origin
      02 remote: Enumerating objects: 12, done.
re…ote: Counting objects:  50% (6/12)
remote: Compressing objects: 100% (3/3), done.
      02 remote: Total 7 (delta 4), reused 7 (delta 4), pack-reused 0
Unpacking objects… 100% (7/7)    (1/7)   
      02 From github.com:%user%/capistrano_sample_app_v1
      02    1b9edaa..78ee082  master     -> master
    ✔ 02 %user%@%hostname% 2.175s
** Execute git:create_release
00:06 git:create_release
      01 mkdir -p /var/www/capistrano_sample_app_v1/releases/*******
    ✔ 01 %user%@%hostname% 0.139s
      02 git archive master | /usr/bin/env tar -x -f - -C /var/www/capistrano_sample_app_v1/releases/*******
    ✔ 02 %user%@%hostname% 0.154s
** Invoke slack:deploy:updating 
** Execute deploy:updating
** Invoke deploy:set_current_revision (first_time)
** Invoke git:set_current_revision (first_time)
** Execute git:set_current_revision
** Execute deploy:set_current_revision
00:06 deploy:set_current_revision
      01 echo "78ee082f5b26fcac1ad4aa25410dcc3d1b32d51e" > REVISION
    ✔ 01 %user%@%hostname% 0.140s
** Invoke deploy:symlink:shared (first_time)
** Execute deploy:symlink:shared
** Invoke deploy:symlink:linked_files (first_time)
** Execute deploy:symlink:linked_files
00:07 deploy:symlink:linked_files
      01 mkdir -p /var/www/capistrano_sample_app_v1/releases/*******/config
    ✔ 01 %user%@%hostname% 0.141s
      02 rm /var/www/capistrano_sample_app_v1/releases/*******/config/database.yml
    ✔ 02 %user%@%hostname% 0.138s
      03 ln -s /var/www/capistrano_sample_app_v1/shared/config/database.yml /var/www/capistrano_sample_app_v1/releases/*******/config/database.yml
    ✔ 03 %user%@%hostname% 0.145s
** Invoke deploy:symlink:linked_dirs (first_time)
** Execute deploy:symlink:linked_dirs
00:07 deploy:symlink:linked_dirs
      01 mkdir -p /var/www/capistrano_sample_app_v1/releases/******* /var/www/capistrano_sample_app_v1/releases/*******/tmp /var/www/capistrano_sample_app_v1/releases/*******/vendor /var/www/capistrano_sample_app_v1/releases/*******/public
    ✔ 01 %user%@%hostname% 0.142s
      02 rm -rf /var/www/capistrano_sample_app_v1/releases/*******/log
    ✔ 02 %user%@%hostname% 0.138s
      03 ln -s /var/www/capistrano_sample_app_v1/shared/log /var/www/capistrano_sample_app_v1/releases/*******/log
    ✔ 03 %user%@%hostname% 0.138s
      04 ln -s /var/www/capistrano_sample_app_v1/shared/tmp/pids /var/www/capistrano_sample_app_v1/releases/*******/tmp/pids
    ✔ 04 %user%@%hostname% 0.143s
      05 ln -s /var/www/capistrano_sample_app_v1/shared/tmp/cache /var/www/capistrano_sample_app_v1/releases/*******/tmp/cache
    ✔ 05 %user%@%hostname% 0.137s
      06 ln -s /var/www/capistrano_sample_app_v1/shared/tmp/sockets /var/www/capistrano_sample_app_v1/releases/*******/tmp/sockets
    ✔ 06 %user%@%hostname% 0.136s
      07 ln -s /var/www/capistrano_sample_app_v1/shared/vendor/bundle /var/www/capistrano_sample_app_v1/releases/*******/vendor/bundle
    ✔ 07 %user%@%hostname% 0.138s
      08 ln -s /var/www/capistrano_sample_app_v1/shared/public/assets /var/www/capistrano_sample_app_v1/releases/*******/public/assets
    ✔ 08 %user%@%hostname% 0.138s
** Invoke deploy:updated (first_time)
** Invoke bundler:install (first_time)
** Execute bundler:install
00:11 bundler:install
      01 RBENV_ROOT=/usr/local/rbenv RBENV_VERSION=2.6.5 /usr/local/rbenv/bin/rbenv exec bundle install --path /var/www/capistrano_sample_app_v1/shared/bundle --jobs 4 --without development test --deployment --quiet
    ✔ 01 %user%@%hostname% 0.701s
** Execute deploy:updated
** Invoke deploy:compile_assets (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:compile_assets
** Invoke deploy:assets:precompile (first_time)
** Execute deploy:assets:precompile
** Invoke deploy:assets:backup_manifest (first_time)
** Execute deploy:assets:backup_manifest
** Invoke deploy:cleanup_assets (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:cleanup_assets
** Invoke deploy:normalize_assets (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:normalize_assets
** Invoke deploy:migrate (first_time)
** Invoke deploy:set_rails_env 
** Execute deploy:migrate
** Invoke deploy:publishing (first_time)
** Execute deploy:publishing
** Invoke deploy:symlink:release (first_time)
** Execute deploy:symlink:release
00:11 deploy:symlink:release
      01 ln -s /var/www/capistrano_sample_app_v1/releases/******* /var/www/capistrano_sample_app_v1/releases/current
    ✔ 01 %user%@%hostname% 0.142s
      02 mv /var/www/capistrano_sample_app_v1/releases/current /var/www/capistrano_sample_app_v1
    ✔ 02 %user%@%hostname% 0.142s
** Invoke deploy:published (first_time)
** Execute deploy:published
** Invoke deploy:env_publising (first_time)
** Execute deploy:env_publising
00:12 deploy:env_publising
      01 cp -a /var/www/capistrano_sample_app_v1/.env /var/www/capistrano_sample_app_v1/releases/*******/.env
    ✔ 01 %user%@%hostname% 0.137s
** Invoke deploy:restart (first_time)
** Execute deploy:restart
00:12 deploy:restart
      01 touch /var/www/capistrano_sample_app_v1/releases/*******/tmp/restart.txt
    ✔ 01 %user%@%hostname% 0.137s
** Invoke deploy:finishing (first_time)
** Execute deploy:finishing
** Invoke deploy:cleanup (first_time)
** Execute deploy:cleanup
00:12 deploy:cleanup
      Keeping 5 of 6 deployed releases on %hostname%
      01 rm -rf /var/www/capistrano_sample_app_v1/releases/*******
    ✔ 01 %user%@%hostname% 0.149s
** Invoke slack:deploy:updated (first_time)
** Execute slack:deploy:updated
** Invoke deploy:finished (first_time)
** Execute deploy:finished
** Invoke deploy:log_revision (first_time)
** Execute deploy:log_revision
00:13 deploy:log_revision
      01 echo "Branch master (at 78ee082f5b26fcac1ad4aa25410dcc3d1b32d51e) deployed as release ******* by %user%" >> /var/www/capistrano_sample_app_v1/revisions.log
    ✔ 01 %user%@%hostname% 0.138s
```

## Rails 5.2以上で気をつけなければならないこと

### require': cannot load such file -- bootsnap/setup (LoadError). が発生する場合

* 少なくともRails 5.2系では `bootsnap` はデフォルトで `Gemfile` に組み込まれている

```ruby:Gemfile
group :development, :test do
  ・・・途中省略・・・

  # Reduces boot times through caching; required in config/boot.rb
  gem 'bootsnap', '>= 1.1.0', require: false
end
```

この状態では staging環境の起動失敗する

```sh:Terminal
$ bundle exec rails c -e staging
Traceback (most recent call last):
  13: from bin/rails:4:in `<main>'
  12: from bin/rails:4:in `require'
  11: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/commands.rb:18:in `<top (required)>'
  10: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/command.rb:46:in `invoke'
   9: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/command/base.rb:65:in `perform'
   8: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
   7: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
   6: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
   5: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/commands/console/console_command.rb:95:in `perform'
   4: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/command/actions.rb:14:in `require_application_and_environment!'
   3: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/command/actions.rb:22:in `require_application!'
   2: from /var/www/capistrano_sample_app_v1/shared/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/command/actions.rb:22:in `require'
   1: from /var/www/capistrano_sample_app_v1/releases/*******/config/application.rb:17:in `<top (required)>'
/var/www/capistrano_sample_app_v1/releases/*******/config/application.rb:17:in `require': cannot load such file -- bootsnap/setup (LoadError)
```

- `config/boot.rb`を書き換える

```ruby:config/boot.rb
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)

require 'bundler/setup' # Set up gems listed in the Gemfile.
begin
  require 'bootsnap/setup'
  env = ENV['RAILS_ENV'] || "development"
  Bootsnap.setup(
    cache_dir:            'tmp/cache',          # キャッシュファイルを保存する path
    development_mode:     env == 'development', # 現在の作業環境、例えば RACK_ENV, RAILS_ENV など。
    load_path_cache:      true,                 # キャッシュで LOAD_PATH を最適化する。
    autoload_paths_cache: true,                 # キャッシュで ActiveSupport による autoload を行う。
    disable_trace:        true,                 # (アルファ) `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`をセットする。
    compile_cache_iseq:   true,                 # ISeq キャッシュをコンパイルする
    compile_cache_yaml:   true                  # YAML キャッシュをコンパイルする
  )
rescue LoadError => e;
end # Speed up boot time by caching expensive operations.
```

* 参考になった技術まとめ
  * [bootsnap/README.jp.md](https://github.com/Shopify/bootsnap/blob/master/README.jp.md)
  * [`require': cannot load such file -- bootsnap/setup (LoadError)](https://github.com/Shopify/bootsnap/issues/73)

## リポジトリ

* 本記事のリポジトリは「[webmaster-patche/capistrano_sample_app_v1](https://github.com/webmaster-patche/capistrano_sample_app_v1)」にて公開しております