Concourse CIのpipelineの書き方

## 概要
Concourse CIでrailsアプリケーションを扱う場合のpipelineの書き方の説明です。

以下の記事が親記事となるので、一緒に読むことをおすすめします。
[Concourse CIでRailsアプリケーションをデプロイする](https://qiita.com/Esfahan/items/af6f5b092fbb2a406210)

## ソース元
こちらのソースの詳細な説明となります。
https://github.com/Esfahan/concourse-ci-pipelines

## 処理の流れ
[Concourse CIでRailsアプリケーションをデプロイする](https://qiita.com/Esfahan/items/af6f5b092fbb2a406210)
こちらの記事にも書きましたが、運用フローは以下のようになります。

1. [Rails Application](https://github.com/Esfahan/rails-sample) レポジトリのstaging、productionブランチにマージ
1. Concourse CIが動く
    - Railsのrspecが実行される
    - Capistranoが実行されてデプロイ

## pipeline
`resources`にgitのレポジトリを定義し、`jobs`で使用するリソースと、tasksを定義しています。
job単位でdockerコンテナが分かれるようです。
taskの詳細内容については、また別ファイルで定義します。

```yaml:concourse-ci-pipelines/rails-sample/pipelines/pipeline.yml
---
resources:
# piplelineのgitレポジトリ
- name: pipelines-repo
  type: git
  source:
    uri: git@github.com:Esfahan/concourse-ci-pipelines.git
    # ~/.concourse/credentials.ymlで定義した定数
    private_key: {{github-private-key}}
    branch: master

# railsアプリケーションのgitレポジトリのmasterブランチ
- name: rails-sample-master
  type: git
  source:
    uri: git@github.com:Esfahan/rails-sample.git
    private_key: {{github-private-key}}
    branch: master

# railsアプリケーションのgitレポジトリのstagingブランチ
- name: rails-sample-staging
  type: git
  source:
    uri: git@github.com:Esfahan/rails-sample.git
    private_key: {{github-private-key}}
    branch: staging

# capistranoのgitレポジトリ
- name: capistrano-sample
  type: git
  source:
    uri: git@github.com:Esfahan/capistrano-sample.git
    private_key: {{github-private-key}}
    branch: master

jobs:
#############
# production
#############
# rspecを実行するコンテナ
- name: rails-job-production
  # rails-sampleレポジトリのproductionブランチに変更がある時のみ実行されるようにするためのタグ
  serial_groups: [ production ]
  # # UI上でJobの結果をログイン不要で公開するかどうか
  public: true
  # taskの内容
  plan:
    # resourceで定義した名前を指定し、コンテナ内でgitのソースを取得する
    - get: pipelines-repo

    # 注釈[1]
    # getで定義した名前は、コンテナ内で、rails-sample-masterのソースのトップディレクトリ名として使われます。
    # 以下の例では、rails-sample-masterのソースのトップディレクトリ名をrails-sampleにするための記述方法です。
    # トップディレクトリ名は、file: pipelines-repo/rails-sample/tasks/build.ymlの中で定義されますが、
    # master、stagingブランチのどちらの場合でも、同じディレクトリ名であるほうがbuild.ymlが簡潔になるための施策です。
    - get: rails-sample # コンテナ内でディレクトリ名として使用する名前を定義
      resource: rails-sample-master # 前述のresourcesで定義した名前を定義
      # このリソースに変更があったらデプロイを実行する
      trigger: true
    # taskの内容が書かれているファイルをincludeします。
    - task: build
      file: pipelines-repo/rails-sample/tasks/build.yml

- name: capistrano-job-production
  serial_groups: [ production ]
  public: true
  plan:
    # 前述のjobであるrails-job-productionで使用したリソースをこちらのコンテナでも使うために、passedを定義します。
    - get: pipelines-repo
      passed: [ rails-job-production ]
    - get: rails-sample
      resource: rails-sample-master
      trigger: true
      passed: [ rails-job-production ]
    - get: capistrano-sample
    - task: deploy
      file: pipelines-repo/rails-sample/tasks/deploy.yml
      # taskで使用する定数を定義
      params:
        ENV: production
        GIT_SSH_KEY: {{github-private-key}}

#############
# staging
#############
# 以下はrails-sampleレポジトリのstagingブランチ用
- name: rails-job-staging
  serial_groups: [ staging ]
  public: true
  plan:
    - get: pipelines-repo
    - get: rails-sample
      resource: rails-sample-staging
      trigger: true
    - task: build
      file: pipelines-repo/rails-sample/tasks/build.yml

- name: capistrano-job-staging
  serial_groups: [ staging ]
  public: true
  plan:
    - get: pipelines-repo
      passed: [ rails-job-staging ]
    - get: rails-sample
      resource: rails-sample-staging
      trigger: true
      passed: [ rails-job-staging ]
    - get: capistrano-sample
    - task: deploy
      file: pipelines-repo/rails-sample/tasks/deploy.yml
      params:
        ENV: staging
        GIT_SSH_KEY: {{github-private-key}}
```

## tasks
pipelineで呼びだされるtaskファイルです。
コンテナ内で実行する内容を定義します。

### railsのrspecを実行するtask

```yaml:concourse-ci-pipelines/rails-sample/tasks/build.yml
---
platform: linux
# 使用するdocker imageを定義
image_resource:
  type: docker-image
  source:
    repository: ruby
    tag: 2.4.1
# piplineのresourcesで定義し、jobsのgetで定義したものをコンテナ内で読み込むために、inputを定義する必要があります
# ちなみに、今回はありませんが、コンテナ内で生成したファイルを出力するには、outputを定義します。
inputs:
  - name: pipelines-repo
  - name: rails-sample
# キャッシュするディレクトリを定義します。
# workerが生きている限り（docker-compose downしない限り）生き続けます。
caches:
  - path: rails-sample/vendor/bundle
# ここで読み込んでいるシェルスクリプトに処理の詳細を記載します。
run:
  path: pipelines-repo/rails-sample/scripts/build.sh
```

### capistranoを実行するtask
こちらもbuild.ymlと同じなので説明は省きます。

```yaml:concourse-ci-pipelines/rails-sample/tasks/deploy.yml
---
platform: linux
image_resource:
  type: docker-image
  source:
    repository: ruby
    tag: 2.4.1
inputs:
  - name: pipelines-repo
  - name: capistrano-sample 
caches:
  - path: capistrano-sample/vendor/bundle
run:
  path: pipelines-repo/rails-sample/scripts/deploy.sh
```

## scripts
taskで呼び出されるシェルスクリプトです。
コンテナの中で実行されます。
実際の処理内容はここに書きます。

### railsのrspecを実行する処理
pipelineの`注釈[1]`に書いた通り、jobsのgetで定義した名前、`rails-sample`がディレクトリ名として使用されているのが分かります。

```yaml:concourse-ci-pipelines/rails-sample/scripts/build.sh
#!/bin/bash
cd rails-sample
bundle install --path vendor/bundle
bundle exec rspec spec
```

### capistranoでdeployを実行する処理
capistranoを使ってデプロイ先にssh接続する必要があるので、最初に、環境変数として渡された秘密鍵の値を、ファイルとして保存し、
fowardingするため、ssh-agentで登録しています。

deployコマンドの`bundle exec cap ${ENV} deploy`では、
`${ENV}`に、pipelineで環境変数として定義した、`production`もしくは`staging`が渡されるようにしてあり、
`${ENV}`の値によってデプロイ先が分けられるようになっています。

```yaml:concourse-ci-pipelines/rails-sample/scripts/deploy.sh
#!/bin/bash
# Create ssh key file
mkdir ~/.ssh/
chmod 700 ~/.ssh
echo "${GIT_SSH_KEY}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa

# ssh-agent
eval `ssh-agent`
ssh-add ~/.ssh/id_rsa
ssh-add -l

# deploy
cd capistrano-sample
bundle install --path vendor/bundle

echo "bundle exec cap ${ENV} deploy"
bundle exec cap ${ENV} deploy
```

以上


## 関連記事
- [Concourse CIの環境構築手順](https://qiita.com/Esfahan/items/0bb62b76d999c30b87e3)
- [Concourse CIでRailsアプリケーションをデプロイする](https://qiita.com/Esfahan/items/af6f5b092fbb2a406210)