capistranoでRailsアプリケーションをデプロイするタイミングでpg_dumpする方法

## 背景

Railsアプリケーションをデプロイしたあとになんかあったら困るから念のためバックアップをとっておきたい(DBはPostgresql)
DBはデプロイ先のlocalhostで動いている想定

## どういうことができるようになるか

- `bundle exec cap production deploy` したタイミングでホームディレクトリにタイムスタンプファイル名でバックアップファイルが作られるようになる

- `bundle exec cap production db:dump` を実行したらホームディレクトリにタイムスタンプファイル名でバックアップファイルが作られるようになる

## 環境

|            | version |
|:-----------|:--------|
| capistrano | 3.6.1   |
| rails      | 5.0.0.1 |
| postgresql | 9.5     |

## 忙しい人のために先に手順だけ書いておく

1. ホームディレクトリの直下に `.pgpass` という名前で以下のようなファイルを作る

    ```text:~/.pgpass
    hostname:port:database:username:password
    ```

1. .pgpassをchmod 600する

    ```bash:bash
    chmod 600 ~/.pgpass
    ```

1. Railsプロジェクトに `lib/capistrano/tasks` ディレクトリがなければ作る

    ```bash:bash
    mkdir -p lib/capistrano/tasks
    ```

1. `lib/capistrano/tasks/db_dump.rake` に下記のようなファイルを作る

    ```rb:lib/capistrano/tasks/db_dump.rake
    require 'sshkit'

    namespace :db do
      desc "Backup your PostgreSQL database to ~/"
      task :dump do
        on roles(:db) do
          dbconf = dbconfig
          dbuser = dbconf['username']
          dbname = dbconf['database']
    
          within '~' do
            execute :pg_dump,
              "-w -U #{dbuser}",
              "#{dbname} > \"$(date +%Y%m%d%H%m%S).sql\""
          end
        end
      end

      def dbconfig
        YAML::load(
          capture :cat, "#{current_path}/config/database.yml"
        )[fetch(:rails_env).to_s]
      end
    end
    ```

`config/deploy/production.rb` に下記を追加

```config/deploy/production.rb
before :deploy, 'db:dump'
```

## これを作る前にわからなかったこと

* pg_dumpでパスワード入力を省略する方法がわからなかった
* capistrano taskをどこに書いたらいいのかわからなかった (ググったらtaskの中身は出てくるけどどこにファイルのパスは書いていないことが多かった)

## 解説

ここでは私が作る前にわからなかったことを中心に説明します

### pg_dumpでパスワード入力を省略する方法

1. デプロイ先のホームディレクトリに　`.pgpass` という名前のファイルを作っておく

    ```text:~/.pgpass
    hostname:port:database:username:password
    ```

1. `.pgpass` の権限を `600` に設定する(600にしていなかったら600にしろと怒られる)

    ```bash:bash
    chmod 600 ~/.pgpass
    ```

1. `pg_dump -w` というように `-w`　オプションを使う



#### postgresqlのドキュメントによれば

```
ユーザのホームディレクトリの.pgpassまたはPGPASSFILEで設定されるファイルは、
接続にパスワードが必要な場合(かつ、他に指定されたパスワードが無かった場合)に使用するパスワードを格納するファイルです。

(〜中略〜)

Unixシステムにおいて、.pgpassの権限はグループ、他者へのアクセスをすべて拒否しなければなりません。
 これはchmod 0600 ~/.pgpassといったコマンドによって行います。 
権限をこれよりも緩くすると、このファイルは無視されます

http://www.postgresql.jp/document/9.5/html/libpq-pgpass.html
```

とのこと

詳細はこのURLを参照のこと
http://www.postgresql.jp/document/9.5/html/libpq-pgpass.html

### capistrano taskをどこに書いたらいいのか

`Capfile` に下記のようなコードがあるように　 `lib/capistrano/tasks`　内の `rake` ファイルを拾ってきているようです

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

### バックアップタスクを実装する

そういうわけで `lib/capistrano/tasks/` ディレクトリが無い場合は作ります

```bash:bash
mkdir -p lib/capistrano/tasks
```

`lib/capistrano/tasks` にrakeファイルを作ります

taskを実装します

```rb:lib/capistrano/tasks/db_dump.rake
require 'sshkit'

namespace :db do
  desc "Backup your PostgreSQL database to ~/"
  task :dump do
    on roles(:db) do
      dbconf = dbconfig
      dbuser = dbconf['username']
      dbname = dbconf['database']
      within '~' do
        execute :pg_dump,
          "-w -U #{dbuser}",
          "#{dbname} > \"$(date +%Y%m%d%H%m%S).sql\""
      end
    end
  end

  def dbconfig
    YAML::load(
      capture :cat, "#{current_path}/config/database.yml"
    )[fetch(:rails_env).to_s]
  end
end
```

これで `bundle exec cap production db:dump` というコマンドが実行できるようになります
このコマンドを実行したらホームディレクトリにタイムスタンプファイル名でバックアップファイルが作られるようになります

あとはデプロイする直前に `db:dump` を実行するよう設定します

`config/deploy/production.rb` に下記を追加

```config/deploy/production.rb
before :deploy, 'db:dump'
```

以上