日時予約をしてAPIを叩いたりコマンドを実行したりするシェルスクリプトを作る

## はじめに
未経験からエンジニアに転職しました。
まだまだわからないことだらけですが、毎日楽しく働いています！

業務や個人開発での発見やエラー解決などをアウトプットしています。


## やること
- 任意の日時を指定して
- APIを叩いたり
- コマンドを実行したりする
シェルスクリプトを作ります。

今回はサンプルとして、
**「日時を指定して、GitHub APIを叩き、プルリクエストをマージする」**
をやっていきます!

例えば、
2022年8月1日にプルリクエストAをマージしたい
なんて時に使います。
（実際はマージをきっかけにCIが走りデプロイされるシステムを想定しています。つまり、日時指定をしてデプロイ、リリースができることを想定しています。）

[Pulls APIを使うと、Pull Requestのリスト、表示、編集、作成、さらにはマージまでも行えます](https://docs.github.com/ja/rest/pulls)

# 結論
~~~shell
echo "${任意のコマンド}" | at -t "${RESERVATION_DATETIME}"
~~~
atコマンドを使って日時指定をして、任意のコマンドを実行します。

# 最終系
~~~shell:test.sh
#!/bin/sh

readonly PERSONAL_ACCESS_TOKEN=XXXXXXXXXXXXXXXXX
PULL_NUMBER=$1
RESERVATION_DATETIME=$2

usage() {
  echo "Usage: $0 [pull request number] [release datetime]" 1>&2
}

if [ $# -ne 2 ]; then
  usage
  exit 1
fi

merge_schedule=`cat <<EOS
curl \
-X PUT \
-H "Accept:application/vnd.github.v3+json" \
-H "Authorization:token ${PERSONAL_ACCESS_TOKEN}" \
https://api.github.com/repos/{OWNER}/{REPO}/pulls/${PULL_NUMBER}/merge
EOS
`
echo "${merge_schedule}" | at -t "${RESERVATION_DATETIME}"
~~~

# 解説
~~~shell
#!/bin/sh
~~~
↑はシェルスクリプトの定型だという認識でOKです。

~~~shell
readonly PERSONAL_ACCESS_TOKEN=XXXXXXXXXXXXXXXXX
PULL_NUMBER=$1
RESERVATION_DATETIME=$2

usage() {
  echo "Usage: $0 [pull request number] [reservation datetime]" 1>&2
}

if [ $# -ne 2 ]; then
  usage
  exit 1
fi
~~~
↑の部分の解説です。
最初の３行で定数を宣言しています。
PERSONAL_ACCESS_TOKENは、GitHubのAPIを叩く際に必要になります。
設定方法は別途調べてください。

~~~
$1 $2
~~~
↑については解説は割愛しますが、簡単にいうとスクリプトを実行するときに引数を渡すことができます。
[参考:シェルスクリプトの$1と$2とは何か？$0との違い](https://prograshi.com/general/shell-script-1-2-0/)

~~~shell
if [ $# -ne 2 ]; then
~~~
↑で、引数が２つより少なかったら、↓の説明を出力するようにしています。

~~~shell
usage() {
  echo "Usage: $0 [pull request number] [reservation datetime]" 1>&2
}
~~~
~~~shell
merge_schedule=`cat <<EOS
curl \
-X PUT \
-H "Accept:application/vnd.github.v3+json" \
-H "Authorization:token ${PERSONAL_ACCESS_TOKEN}" \
https://api.github.com/repos/{OWNER}/{REPO}/pulls/${PULL_NUMBER}/merge
EOS
`
~~~
↑では、GitHubのAPIをmerge_scheduleに格納しています。
[GitHub API Merge a pull request](https://docs.github.com/ja/rest/pulls/pulls#:~:text=Status%3A%20204-,Merge%20a%20pull%20request,-Works%20with%20GitHub)

::: note warn
↑のリンクではパーソナルアクセストークンをヘッダーに渡すように書いてありませんが、実際はヘッダーに渡す必要があります。
:::

GETリクエスト以外はパーソナルアクセストークンが必要になるようです。
[参考：GitHub API v3でPOSTリクエストをする際はPublic Repositoryであっても権限（アクセストークン）が必要だった](https://dev.classmethod.jp/articles/when-posting-data-with-github-api-v3-permission-access-token-was-required-even-in-public-repository/)


~~~shell
merge_schedule=`cat <<EOS
~~~
catにヒアドキュメントを渡しています。
ここの解説は以下をご覧ください。
抜粋して引用します。
> ヒアドキュメントを理解するうえで重要なのは、ヒアドキュメントは標準入力として扱われる、ということです。
文字列リテラルでないことに注意してください。
これを把握していれば、最初の例でcatにヒアドキュメントを渡していた理由がわかるかと思います。
catはファイルを省略した場合に標準入力から読むため、ヒアドキュメントの内容が標準出力に出ることになります。
逆に下記のようにechoに渡しても内容が標準出力に出ることはありません。echoは標準入力から読まないし、
ヒアドキュメントはリテラルではないからです。

[参考：bashのヒアドキュメントを活用する](https://qiita.com/take4s5i/items/e207cee4fb04385a9952)

~~~shell
echo "${merge_schedule}" | at -t "${RESERVATION_DATETIME}"
~~~
最後の部分です。
atコマンドを使用して、APIを叩きます。
atコマンドについては以下をご覧ください。
[参考：atコマンドの使い方
](https://www.server-memo.net/tips/at.html#:~:text=30%3A00%202017-,echo%20%22%E5%AE%9F%E8%A1%8C%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%22%20%7C%20at%20%22%E6%97%A5%E6%99%82%22,-%E3%80%8Cecho%E3%80%8D%E3%81%A7%E6%A8%99%E6%BA%96)

また、Macではatコマンドを使うために別途設定が必要です。
[参考：Macでatコマンドが実行できないときの対処法](https://qiita.com/shge/items/6c43947a77abd9d2d1b2)

## 実行してみる
では、最後に実行してみます。

シェルスクリプトがあるディレクトリで、
~~~shell
$ sh test.sh 30 202208011200
~~~
上記のようにコマンドを入力して実行します。
30は、プルリクエストのナンバ−です。
202208011200は、2022年8月1日12時00分を指定して、コマンドが実行するようにしています。

つまり、上記コマンドの狙いは、
#### 2022年8月1日12時00分にプルリクエスト３０をマージする
です。

上述しておりますが、
このプルリクエストのマージを契機に、CIが作動してデプロイまで完了するようになっています。
こうすることで、指定した時間にデプロイを予約することができました。


::: note warn
at コマンドは指定した時間から０〜３０分程度のラグがあってから実行されます。厳密に時間を指定したい場合は不適です。
:::


## 最後に
実務経験２ヶ月弱のエンジニアが書いたものです。
しかも初めてシェルスクリプトを作ったという状態です。

間違っていたり非効率な部分があるかもしれません。
指摘などあればぜひ教えていただきたいです！

ただ、何かの参考になったら嬉しいです。
ここまで読んでいただきありがとうございました！