CircleCIでhubコマンドを活用してみた

Qiita初投稿です！よろしくお願いします :bow_tone1: 

# TL;DR

この記事を読むと、あるレポジトリで出力されるアウトプットを、別リポジトリ向けに反映するPull Requestを自動で作成できるようになります。

リポジトリ間で依存関係があったりすると使い道が見出せるかもしれません。

# 1. やったこと

* 課題
  Aレポジトリで修正して、Pull Requestがmasterブランチにマージされた後に[webpack](https://webpack.js.org)して、Bレポジトリに反映するPull Requestを都度手動で実施するのが結構手間。時々、Bレポジトリに反映するのを忘れて、あれ...みたいな事態に陥りがち。

* 解決策
  CircleCIがmasterブランチにマージされる度にうまいことやってくれるようにする。

# 2. hubコマンドをCircleCIで使って解決する

## hubコマンドとは？

[hub](https://hub.github.com/)コマンドは、GitHubが提供しているコマンドラインツールです。gitコマンドを拡張して、GitHubの様々な機能が使えるようになります。

今回は、hubコマンド([v2.13.0](https://github.com/github/hub/tree/v2.13.0))を使って、CLIでPull Requestを作成します。

# 3. 実行手順

## Pull Requestを作成するためのシェルスクリプトを作成する

### create-pull-request.sh

```bash
#!/bin/bash

set -ex

LOCAL_REPO=path/to/some_repo
REMOTE_REPO=organization/some_repo
COPY_FROM=path/to/copy_from
COPY_TO=path/to/copy_to
NEW_BRANCH=some_branch_name
COMMIT_HASH="$(echo $CIRCLE_SHA1 | cut -c -7)"
PR_TEMPLATE=$HOME/target/.github/CIRCLECI_PULL_REQUEST_TEMPLATE.md

# install hub command
wget https://github.com/github/hub/releases/download/v$HUB_VER/hub-linux-amd64-2.13.0.tgz
tar zvxvf hub-linux-amd64-2.13.0.tgz
sudo ./hub-linux-amd64-2.13.0/install
rm -rf hub-linux-amd64-2.13.0

# git clone
mkdir -p $REPO_DIR
hub clone $REPO_LOCATION $REPO_DIR

# 反映したい内容をコピー
cp -fp $COPY_FROM $COPY_TO

# git configの設定
git config --global user.email "circleci@example.com"
git config --global user.name "circleci"

# 反映したい内容をCommit
cd $REPO_DIR
git checkout -b $NEW_BRANCH
git add . && git commit -m "update for $COMMIT_HASH"

# Pull Request Templateを編集
## 作成するPull RequestのタイトルにCommit Hashを付ける
sed -ie "s/hoge$/hoge($COMMIT_HASH)/" $PR_TEMPLATE

## 作成するPull Requestの内容に作成元のPull Requestを紐付ける
echo -e "$CIRCLE_PULL_REQUEST" >> $PR_TEMPLATE

# Pull Requestを作成
hub pull-request -F $PR_TEMPLATE -b develop -p
```

シェルスクリプトには、実行権限を付けることをお忘れなく。

### CIRCLECI_PULL_REQUEST_TEMPLATE.md

```markdown
update: for hoge

## Reference pull request
```

`hoge`は、シェルスクリプトにて`hoge(commit_hash)`に置き換えられ、テンプレートの行末にCircleCI実行対象のPull RequestのURLを追記するようにしています。

## CircleCIにシェルスクリプトを組み込む

### config.yml (CircleCI)

本稿の対象となるconfig.ymlの一部を抜粋して以下に記載します。

```yaml
version: 2

references:
  ## branch filters
  filter_only_master: &filter_only_master
    branches:
      only:
        - /^master/

jobs:
  create_pull_request:
    docker:
      - image: some-docker-image
    steps:
      - checkout
      - run:
          name: create pull request
          command: ./.circleci/create-pull-request.sh

workflows:
  version: 2
  build_and_deploy:
    jobs:
      - create_pull_request:
          filters: *filter_only_master
```

# 4. ちょっと苦労したところ

hub pull-requestコマンドは、初回実行時に username と password を以下のように聞かれてしまう。

```bash
$ hub pull-request -F PULL_REQUEST_TEMPLATE.md
github.com username:
github.com password for user (never stored):
two-factor authentication code:
```

これを解決するために以下を行うことで解決できました。

- CircleCIのEnvironment Variablesに`GITHUB_USER`と`OAUTH_TOKEN`を追加する
- シェルスクリプトに以下を追記する

```bash
cat << EOF > ~/.config/hub
github.com:
- user: $GITHUB_USER
  oauth_token: $OAUTH_TOKEN
  protocol: https
EOF
chmod 600 ~/.config/hub
```

これは、`hub pull-request`コマンドを初回に実行した際に生成されるものを手動で作成したものですが、シェルスクリプトに書くのはちょっとダサい。。。と思ったので、もう少し調べてみると以下のような記述を見つけました。

```bash
$ man hub
:(snip)
CONFIGURATION
   GitHub OAuth authentication
       Hub will prompt for GitHub username  password the first time it needs to access the API and exchange it for an OAuth token, which it saves in ~/.config/hub.

       To avoid being prompted, use GITHUB_USER and GITHUB_PASSWORD environment variables.

       Alternatively, you may provide GITHUB_TOKEN, an access token with repo permissions. This will not be written to ~/.config/hub.
:(snip)
```

なるほど。。。そこで、最終的には、以下を行うことで解決しました。

- CircleCIのEnvironment Variablesに`GITHUB_TOKEN`を追加する

これで、初回実行時でも username と password を聞かれることなく、`hub pull-request`を実行することができました。

# 5. 工夫したところ

Pull Request間の対応関係を分かりやすくするために以下を行いました。

- Templateのタイトルにハッシュ値を付けて、Pull Requestを識別できるようにした
- Templateの内容に元のPull RequestのURLを追記して、Referenceできるようにした

# 6. 今後やりたいこと

- Hotfix等の対応の場合に、ベースブランチを変えられるようにしたい！
- Commitした内容を判定して、反映する内容を変えられるようにしたい！

# 7. まとめ

今回は、CircleCIとhubコマンドの一部を使ってみましたが、使い方によっては色々柔軟にできそうだなという感じでした。他にも使い場面がありそうなので使っていこうと思います。

以上！