【Mac】シェルスクリプトをmacOS App化してみる

# はじめに
なんやかんや作業の自動化にシェルスクリプトをよく使うのですが、いちいちターミナルを開いて`.sh`ファイルをドラック&ドロップしてっていうのすらめんどくさくなったので、ダブルクリックで実行できる`.app`ファイル化してみました。
仮想環境を複数立ち上げるのとかがめんどくさくて行き着いたとかそーゆーレベル感の話になりますので必要最低限のことしか書きません。

macOSにはAutomator.appとう自動化ツールつくれーるがあるんですけど今回は使いません。~~(イマイチ使い方がわかっていない)~~
Automatorを使った方法は下記の記事などを参考にしてください。
<参考>
[Macでシェルスクリプトを .appアイコン化する方法](https://qiita.com/KurosawaTsuyoshi/items/425cd484e8d7759af460)(@KurosawaTsuyoshi さんの記事より)

# 使用環境
macOS 10.13.6
あまり関係ないと思いますが。
 
# .Appファイルのお作法的なやつ
なんでも良いのでまずは`.app`ファイルの中身をみてみます。
macOSだとチェス.appというのがデフォルトで入っているかと思うので、とりあえずそれを開いてみます。
アプリケーション > チェス.app を右クリック > "パッケージの内容を表示" を選択します。
するとこんな感じで中身を確認することができます。
<img width="1029" alt="スクリーンショット 2019-11-05 15.02.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268331/da998bb3-97a6-f516-30e0-5739e231e907.png">

基本的にmacOS用のアプリケーションは主に、

- 〇〇.app
    - Contents
        - Info.plist
        - MacOS
        - Resources

から構成されています。

**Info.plist**：アプリケーションに関する設定などを記述するファイル。多分、最初に読み込まれる。
**MacOS**：ここに実行するシェルスクリプトファイルを突っ込みます。
**Resources**：ここにアプリのアイコン(.icns)や使用する画像などを突っ込みます。

# 準備
ここからもろもろを準備していきます。
サンプルとして、"Hello, World!"を表示させるアプリケーションを作っていきます。

## 器の準備
好きなところ(デスクトップとか)に 右クリック > "新規フォルダ" でフォルダを作成し、Hello.appなどお好きなアプリ名にリネームします。
今回はデスクトップに作成した前提で進めます。
作成したHello.appを 右クリック > "パッケージの内容を表示" で、中身を開き、"Contents"という名前のフォルダを同様に作成します。
同様に"Contents"の中身に"MacOS"と"Resources"というフォルダを作成します。
<img width="882" alt="スクリーンショット 2019-11-05 16.12.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268331/909f3cac-4e88-f5ea-5a84-4b17aa6aa727.png">
器はこんな感じになります。
ターミナルで下記を実行してもOKです。

```shell:mkdir.sh
#!/bin/bash
mkdir ~/Desktop/Hello.app
mkdir ~/Desktop/Hello.app/Contents
mkdir ~/Desktop/Hello.app/Contents/MacOS
mkdir ~/Desktop/Hello.app/Contents/Resources
```

先にHello.appという名前にしてますが、適当な名前のフォルダの状態で進めて一番最後にHello.appにリネームしてもOKです。(むしろそっちの方がいいかも)

## 実行するシェルスクリプトの準備
実行させたい`.sh`ファイルを`~/Desktop/Hello.app/Contents/MacOS/`に用意します。
下記シェルスクリプトをテキストエディットなどにコピペし、`~/Desktop/Hello.app/Contents/MacOS/`の中に`Hello.sh`という名前で保存してください。
`Hello.app`の中に直接保存できない場合は、デスクトップなどに一度保存してから指定の場所に移動してください。

```shell:Hello.sh
#!/bin/bash

osascript -e 'display dialog "Hello,World!" with title "Hello,World!" with text buttons {"Hello","World!"}'
exit 0
```

保存後、`chmod +x ~/Desktop/Hello.app/Contents/MacOS/Hello.sh`をターミナルで叩いてスクリプトの実行権限を付与しておきます。

## Info.plistの準備
Info.plistを作成していきます。
下記テキストをテキストエディットなどにコピペし、
`~/Desktop/Hello.app/Contents/`の中に**必ず`Info.plist`という名前で保存**してください。
`Hello.app`の中に直接保存できない場合は、デスクトップなどに一度保存してから指定の場所に移動してください。
いろいろな情報を書き込むことができますが、今回は実行するシェルの情報のみを`CFBundleExecutable`というキーに対応させて書き込みます。

```xml:Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleExecutable</key>
	<string>Hello.sh</string>
</dict>
</plist>
```
## ここまでの中身はこんな感じ
<img width="882" alt="スクリーンショット 2019-11-05 19.07.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268331/170aa6bd-3ee5-0229-ebba-e6eb252667d5.png">

ここまでの設定が終了した段階で、`Hello.app`をダブルクリックすると動作するようになっているかと思います。

# (おまけ)アイコンを設定する
上記までの設定で、シェルスクリプトの実行自体はできるようになりましたが、アイコンがデフォルトのままなので変更する場合の設定を記載しておきます。

## .icnsファイルの作成
アイコンとなる`.icns`ファイルを作成していきます。
地味にこの`.icns`ファイルを作るのがめんどくさいです・・・

<参考>
[Macアプリの.icnsを作るときのメモ](https://qiita.com/Jacminik/items/a4c8fe20a4cba62f428b)(@Jacminik さんの記事より)
[Apple DeveloperサイトのIconに関するところ](https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/app-icon/)

1. `AppIcon.iconset`という名前のフォルダを作成します。
2. 適当に**512×512サイズか512×512@2xサイズのPNGファイル**を用意します。
3. プレビュー.appで開きます。
4. ツール > サイズを調整 から各サイズのファイルに調整し、`AppIcon.iconset`に下記ファイル名で書き出します。`@2x`となっているものはファイル名のサイズを倍にする or 解像度を144に設定して書き出してください。
    - icon_512x512@2x.png
    - icon_512x512.png
    - icon_256x256@2x.png
    - icon_256x256.png
    - icon_128x128@2x.png
    - icon_128x128.png
    - icon_32x32@2x.png
    - icon_32x32.png
    - icon_16x16@2x.png
    - icon_16x16.png

ちなみに全てのサイズを揃えなくてもicnsは作れるのでよしなにやってください。
私は`@2x`の作成をサボりました・・・

5.ターミナルで下記コマンドを実行してiconsetをicnsファイルに変換します。

```shell:ターミナル 
# AppIcon.iconsetのパスに移動(ここではデスクトップに作成した場合を例にしてます)
$ cd ~/Desktop
# AppIcon.iconsetからicnsファイルを作成
$ iconutil -c icns AppIcon.iconset
```

6.デスクトップに作成されたAppIcon.icnsを`~/Desktop/Hello.app/Contents/Resources`にコピー

以上でicnsファイルの準備は終了です。


***<19/11/05追記>***
[Macアプリの.icnsを作るときのメモ](https://qiita.com/Jacminik/items/a4c8fe20a4cba62f428b)の@narumi18wa さんのコメントにとても良きシェルスクリプトを見つけたので貼っておきます。
sipsコマンドを利用してリサイズ&リネームしてくれる感じです。
cdでpngのあるディレクトリに移動してから実行してください。
実行するとpngのファイル名を聞かれるので、**拡張子なしのファイル名**を入力してEnter。
一瞬で1枚のpngからicnsにしてくれるのでとても幸せ・・・

```shell:pngToIcns.sh

#!/bin/bash

echo -n "pngのファイル名(拡張子なし):"
read filename

array=(16 32 128 256 512)

mkdir "${filename}.iconset"
for i in "${array[@]}"
do
    sips -Z $i ${filename}.png --out "${filename}.iconset/icon_${i}x${i}.png"
    sips -Z $((i*2)) ${filename}.png --out "${filename}.iconset/icon_${i}x${i}@2x.png"
done

iconutil -c icns "${filename}.iconset"
rm -rf "${filename}.iconset"
```

## Info.plistにアイコン設定情報を追記
[Info.plistの準備](#infoplistの準備)で作成したInfo.plistに追加する形で記述を増やします。
`CFBundleIconFile`というキーに対して設定するicnsファイルの名前を設定します。(AppIcon.icnsまで設定してもOKです。)

```xml:Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleExecutable</key>
	<string>Hello.sh</string>
	<key>CFBundleIconFile</key>
	<string>AppIcon</string>
</dict>
</plist>
```
<br>
ここまで設定して保存をするとアプリケーションのアイコンが変わるはず・・・なのですが、キャッシュやらなんやらが関係してすぐに反映されないことがあります。というか多分ダメ・・・
いくつか方法はありますが、一番簡単なのは`.app本体`を適当なところにコピペするとコピーした方は意図したアイコンに変更されていると思います。


# おわりに
なんとなくカッコ良さそうだからという理由だけでApp化しましたが、シェルの中身を変えるだけでいろいろなものに使えると思うので気が向いたら試してみてください！

おわり。