シェルスクリプトでスキャフォールドツール作った話

[アクシス Advent Calendar 2020](https://qiita.com/advent-calendar/2020/mediaxis) 10日目担当のmichidaです!!

前日まで全く記事書いてなくて焦ってます。
ちゃんと書き終えることが出来るのか。。

ところでみなさん自動化してますか？
オフは自己学習に努めるべきと言われたりしますが、その為には業務を円滑に進めれるようにしておかないといけないですよね。
円滑に進められるように業務で使えるツールを作り、気持ちよく自己学習をしていくというアプローチがあるというのをどこかで見ました。


それはさておき、現在、私が参画しているSalesforceプロジェクトの中で毎回毎回行う面倒な作業があったので自動化ツールを作りました。
今回はその記事を書こうと思います。

私が作った自動化ツールは、所謂スキャフォールドツールというみたいです。

# スキャフォールドとは

[goo辞書](https://dictionary.goo.ne.jp/word/%E3%82%B9%E3%82%AD%E3%83%A3%E3%83%95%E3%82%A9%E3%83%BC%E3%83%AB%E3%83%89/)によると

> 足場材料

です。まぁなんか土台みたいなものです。
なのでスキャフォールドツールとは、0から作る時に `土台だけ` を用意してくれるツールと思ってください。


# どんなことを自動化したのか

現在、プロジェクトは初期開発フェーズでして、絶賛新規画面盛り盛り開発中です。

その為、まずタスクに着手した場合は、

- Controllerを作成
- BaseControllerを作成
- Serviceを作成
- ViewModelを作成
- 親pageを作成
- 子PCpageを作成
- 子SP(スマートフォン)pageを作成

と最大７ファイルを作るという作業が発生します。
とても多いですね。

Visual Studio CodeにSales forceの拡張機能を入れればコマンドパレットからクラスを作成であったり、ページを作成であったりすることができて、それなりに楽にはできるのですが、作り始めはやっぱり毎回毎回するのがめんどくさい。。

さらにpageなどはガワが決まってて他のファイルから同じところをコピペして、その機能用に呼び出しコントローラを変えたりするなど、ちまちました作業もありとてもめんどくさい。。
やっぱり人間はミスをする生き物。変更漏れがあったりしますよね。

その作業をコマンド叩くことでそれぞれ(ちょっと気を効かせて)用意してくれるツールを作りました。

# 動作画面

michidaaaaaaaaaで作りました。

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190685/9ca708ba-61ad-5fa1-6f5f-3d31fdd508ef.gif)


# 入力待ち状態
まず入力待ち状態（reat -p）にしてファイル名を入力するようにしています。

```shell
read -p "作成するファイル名を入力してください : > " urlName
read -p "SPページを作成しますか？ (y/n) > " doCreateSPpage
```

urlNameとdoCreateSPpageという変数に受け取ります。
SPページは画面により作成したりしなかったりするのでy/nで任意に作るかどうかを決めるようにしています。

ファイルを作るのはSales forceのSFDXコマンドに任せています。

```shell
echo "========================================="
echo ${urlName}Controller
echo "========================================="
sfdx force:apex:class:create -n ${urlName}Controller -d force-app/main/default/classes
```
-n ファイル名
-d 出力先


# ファイルの中身も書き換える

SFDXコマンドで作るとこんな中身の空ファイルが出来上がります。

```html:page
<apex:page>
<!-- Begin Default Content REMOVE THIS -->
<h1>Congratulations</h1>
This is your new Page
<!-- End Default Content REMOVE THIS -->
</apex:page>
```

前述しましたが、pageはある程度構造が決まっています。

↓こんな感じ

```html:page
<apex:page controller="BaseController" showheader="false" sidebar="false" applyHtmlTag="false" applyBodyTag="false" docType="html-5.0"
    language="ja" standardStylesheets="false" >
    <apex:composition template="{!Template}">
        <apex:define name="title">michidaaa登録画面</apex:define>
        <apex:define name="body">
            <c:HeaderPC rendered="{!!IsSP}" />
            <c:HeaderSP rendered="{!IsSP}" />
            <c:SideNavPC rendered="{!!IsSP}" />
            <c:SideNavSP rendered="{!IsSP}" />
            <apex:include rendered="{!!IsSP}" pageName="michidaaaaaaaPC" />
            <apex:include rendered="{!IsSP}" pageName="michidaaaaaaaSP" />
        </apex:define>
        <apex:define name="js">
            <script>
            </script>
        </apex:define>
    </apex:composition>
</apex:page>
```

なので共通となるところは最初から記述されている形にしました。
どうやって書いているかというとリダイレクトで愚直にやってるだけなんです。
このフェーズだけで使う物なんでパワープレイです。

```html
echo "<apex:page controller='BasePageController' showheader='false' sidebar='false' applyHtmlTag='false' applyBodyTag='false' docType='html-5.0' language='ja' standardStylesheets='false' id='${fileName}'>" > $parentPage
echo "    <apex:composition template='{!Template}'>" >> $parentPage
〜 省略　〜
```

# オプション
BaseContorollerもSPと同じように任意で作るかどうか決めていたのですが、作成頻度は低いのでデフォルトでは作らずオプション(-b)をつけることでファイルを作るかどうか決めれるようにしました。

```sh
if [[ "$1" =~ -*b ]]
then
    read -p "BaseController名を入力してください。 例: OrderInputの場合、Orderを入力 > " baseUrlName
    doCreateBaseController="y"
fi
```
こちらのコードは、正規表現でチェックしています。

`if [[ "$1" =~ -*b ]]`

- sh createInitFile.sh -b とコマンドが叩かれた場合に一つ目の引数を$1で取得することが出来ます
- 正規表現を使うために、ブラケットを二重にしています
- 正規表現でチェックするときの演算子は"=~"になります

`-*b`
一文字目は-(ハイフン)であることを前提にしてます。
オプションは他にも用意してあり、それが順不同だった場合でも受付ることが出来るように-*(ハイフンにアスタ）をつけてチェックするようにしました。

# 置換
あとは、ツールのなかでAPI Version情報も書き換えました。
Sales forceはファイル一つ一つに対してバージョン情報を持っているのですが、丁度開発していた時にSummer'20からWinter'21（なんかバージョンのエイリアスなんですけどカッコよくない？）にバージョンが上がりました。

ただ、Sandbox(動作確認環境)によってはデプロイできないという事が起きたので、一旦、49に統一しようということになり、レビュールールに以下のルールが追加されました。

```
apiVersionを49 (Summer '20)にしておいて欲しいです。
他sandboxで49のものがあったりして50(Winter '21)だとデプロイ出来ないという状況になるため。

Winter '21について↓
https://help.salesforce.com/articleView?id=000354530&language=ja&mode=1&type=1
```

とはいえやっぱり変更が漏れたりしますね。
その都度、レビュアーの指摘と修正が発生し、微量ながら工数を削られてました。
なのでバージョン50だった場合は49にするように置換することにしました。

```shell
function replace() {
    targetFile=${1}
    fromWord=${2}
    toWord=${3}
​
    cat ${targetFile} | sed -e "s/${fromWord}/${toWord}/" > ${targetFile}.bak
    mv ${targetFile}.bak ${targetFile}
}

// replase関数呼び出し。引数を三つ渡す。
replace "${targetFile}-meta.xml" "50.0" "49.0"
```

catで出力したものを置換して同ファイルを更新というのを一度で出来ないため、一旦バックアップファイルとして作成し、それをmvで更新したいファイル名にすることで入れ替えてます。

# スキャフォールドツールを作った結果

- ファイル作成の手間が無くなり幸せ
- 設計を統一することができるため幸せ


# まとめ
ほんとにただツール作った話なので誰得って感じの記事ですが、ちょっとでも参考になれば幸いです。


# PS.
自動化したツールを作ってくれたメンバーがいたら惜しみなく感謝を伝えましょう。
にんまりになります。