CodeCommitにPR飛ばすシェルを書いてみた

## 概要

シェルを使うのはコマンドのオプションが長くなったとき、備忘のためにコマンドの羅列をファイルに記すだけだった。これをもう少し汎用的なものにしたかった。CodeCommitにPRを飛ばすシェルを例にしながら、調べてわかったことを説明します。

この記事を読むことでコマンドの羅列を詰め込んだファイルを実行するだけ（下記）から

```bash
bash main.sh
```

コマンドっぽいものを実行（下記）できるようになるかも

```bash
prlauncher --profile stg launch
```

## 前提

開発PCがMacなので、Macで動くBashを前提にこの記事を書いています。普段使っているコマンドを詰め込んだスクリプトなので、皆さんの手元では動かないかもしれません。必要なコマンドの一覧を後述するのでインストール後に実行してみてください。

## 作るもの

CodeCommitにPR飛ばすスクリプトは下記です。使い方は任意のリポジトリに移動してコマンドを叩くとどのブランチからどのブランチへPRを飛ばしたいのかを対話形式で選べるものです。PRを送るとchangeのURLを開いてくれるのでコピペしてレビュアーにコードをレビューしてもらいましょう。

```bash
#!/bin/bash
VERSION="$(basename ${0}) version 0.0.1 ";
function usage {
    cat <<EOF
----------------------------------------------------------------------
 ______         __ __   ______                                 __   
|   __ \.--.--.|  |  | |   __ \.-----.-----.--.--.-----.-----.|  |_ 
|    __/|  |  ||  |  | |      <|  -__|  _  |  |  |  -__|__ --||   _|
|___|   |_____||__|__| |___|__||_____|__   |_____|_____|_____||____|
                                        |__|                        
 _____                           __               
|     |_.---.-.--.--.-----.----.|  |--.-----.----.
|       |  _  |  |  |     |  __||     |  -__|   _|
|_______|___._|_____|__|__|____||__|__|_____|__|  
                                                  
----------------------------------------------------------------------

$(basename ${0}) is a tool for ...

Usage:
    $(basename ${0}) [command] [<options>]

The commands are:
    launch    Launch pull request interactively!

Options:
    --version, -v     print $(basename ${0}) version
    --help, -h        print this

EOF

check
}

function version {
    echo $VERSION
}

function launch {
    cat <<EOF
----------------------------------------------------------------------
 ______         __ __   ______                                 __   
|   __ \.--.--.|  |  | |   __ \.-----.-----.--.--.-----.-----.|  |_ 
|    __/|  |  ||  |  | |      <|  -__|  _  |  |  |  -__|__ --||   _|
|___|   |_____||__|__| |___|__||_____|__   |_____|_____|_____||____|
                                        |__|                        
 _____                           __               
|     |_.---.-.--.--.-----.----.|  |--.-----.----.
|       |  _  |  |  |     |  __||     |  -__|   _|
|_______|___._|_____|__|__|____||__|__|_____|__|  
                                                  
----------------------------------------------------------------------

EOF
    git remote -v >> /dev/null 2>/dev/null
    if [ ! $? = "0" ]; then
        echo [ERROR] $(git remote -v)
        exit 1;
    fi;

    repo_name=$(git remote -v |head -n 1|sed 's?.*v1/repos/??'|cut -d ' ' -f1)

    read -p "type your title of pull request: " title

    branches=($(git ls-remote origin | sed 's?.*refs/heads/??' | tail -n +2))
    echo "\nQ1: Which is target_branch ? (\"target_branch\" <- request_branch):"
    echo ${branches[@]} | tr ' ' '\n' | nl -v 0
    read -p "Select number (0...$((${#branches[@]}-1))): " num
    echo ""
    target_branch=${branches[$num]}
    
    echo "\n\nQ2: Which is request_branch ? ($target_branch <- \"request_branch\"):"
    echo ${branches[@]} | tr ' ' '\n' | nl -v 0
    read -p "Select number (0...$((${#branches[@]}-1))): " num
    echo ""
    request_branch=${branches[$num]}
    
    echo "\n\nQ3: Do you want pull request there branches below?"
    echo "----"
    echo "repositoryName : $repo_name"
    echo "title          : $title"
    echo "branch         : $target_branch <- $request_branch"
    echo "description    : Comming soon..."
    echo "----"
    read -p "yes or no :" ans
    
    case "$ans" in
        yes)
            response=$(aws codecommit create-pull-request --title "$title" --targets repositoryName=$repo_name,sourceReference=$request_branch)
            if [ ! $? = "0" ]; then
                echo "[ERROR] aws command error";
                exit 1;
            fi;
            echo $response|jq;
            pr_id=$(echo $response | jq -r .pullRequest.pullRequestId)
            region=$(aws configure get region)
            echo "[Success] PullRequestURL is below."
            echo "https://${region}.console.aws.amazon.com/codesuite/codecommit/repositories/${repo_name}/pull-requests/${pr_id}/changes?region=${region}"
            open https://${region}.console.aws.amazon.com/codesuite/codecommit/repositories/${repo_name}/pull-requests/${pr_id}/changes?region=${region}
        ;;
        
        no)
            echo "OK."
        ;;

        *)
            echo "[ERROR] retry please. see you"
            exit 1
        ;;
    esac
}

function check_cmd {
    if !(type "$1" > /dev/null 2>&1); then
        echo "[ERROR] not found command $1. Please install $1."
        exit 1;
    fi;
}

function check {
    check_cmd git
    check_cmd aws
    check_cmd jq
}



case ${1} in
    launch)
        $1
    ;;
    help|--help|-h)
        usage
    ;;

    version|--version|-v)
        version
    ;;
    
    *)
        usage
        exit 1
    ;;
esac
```

いくつかの部品に分けて説明をしていきます。

## #0 コマンドっぽくしたい

なにか色々やりたいことを詰め込んだコマンドの塊を下記で実行するのをまずはやめたいと思います。コマンドっぽくしましょう。

```shell
bash main.sh
```

コマンドっぽさといえば、私は下記のようなものをイメージします。

1. どこからでも実行できる
2. 使い方が標準出力される
3. オプションを設定できる
4. サブコマンドがある
5. 対話形式でよしなにやりたいことをやってくれる
6. ちょっとかわいい標準出力

一つずつ説明していきたいと思います。

## #1 どこからでも実行できるようにする

~~bash~~ main~~.sh~~

まずは実行するときに`bash`と`.sh`を消しましょう。

下記の通りscriptファイルの先頭に`#!bin/bash`と追記します。

```bash : main.sh
#!/bin/bash
echo "prlauncher script"
```

ファイル名をリネームしましょう

```bash
mv main.sh prlauncher
```

実行権限を与えましょう

```bash
chmod +x prlauncher
```

シンボリックリンクをはりましょう

```bash
ln -s /var/local/bin/prlauncher $(pwd)/prlauncher
```

これでどこからでも実行できます。好きなディレクトリに移動してprlauncherを入力して実行してみてください。

```bash
cd ~
prlauncher
```

下記のように出力されたら成功です。コマンドを修正したいときは、従来どおりのパスのファイルを変更すれば良いです。

```bash
prlauncher script
```

## #2 使い方が標準出力されるようにする

コマンドだけ打ち込んだ場合や-h, --help, helpといったサブコマンドを打ち込むと使い方が表示されるようにしましょう。

まずはコマンドを打ち込んだときに使い方が出力されるようにします。

```bash:prlauncher
#!/bin/bash

function usage{
cat <<EOF
$(basename ${0}) is a tool for ...

Usage:
    $(basename ${0}) [command] [<options>]

The commands are:
    launch    Launch pull request interactively!

Options:
    --version, -v     print $(basename ${0}) version
    --help, -h        print usage
    --profile         select profile (aws-cli)

EOF
}

usage
```


`usage` というfunctionを定義して、最終行で定義したusage関数を呼び出しています。`$(basename ${0})` を用いることで実行しているファイル名を参照することができます。（今回の場合はprlauncherが出力されます）使い方は、自分が忘れたときに思い出せるように書きましょう。

記載した使い方に従って実装を進めていきます。

続きます。