GAS用のCLIツール clasp を使ってGASをローカルで開発して実行するの巻。

# claspを使ってローカルでGASを書く
基本的にGASの開発はブラウザで行う必要があります。
<img width="1439" alt="スクリーンショット 2019-07-07 15.03.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/6b584e83-647c-8e8b-7b50-9270d9c4cc70.png">
これは面倒ですね。
単純なプログラムであればブラウザでも十分ですが、ある程度複雑な開発になるとこんな不満が出そうです。

- Gitでのバージョン管理がしたい
- 使い慣れたエディタで開発したい

この不満を解消するべくGoogleが開発したのが [google/clasp](https://github.com/google/clasp)です。
`clasp` はローカルで書いたコードをGitHub感覚でpush、オンラインからpullできるCLIツールです。

今回はこの`clasp`を使ってローカルでGASの開発、一歩踏み込んでデバッグまでできるようにしてみます。
ついでにローカルではTypeScriptを使うことにします。

# ローカル環境で快適なGAS開発を行う手順

## GAS API の設定をオンにする
はじめに[G Suite ユーザー設定画面](https://script.google.com/home/usersettings)からGoogle Apps Script APIをオンにして下さい。
<img width="1429" alt="スクリーンショット 2019-07-07 16.03.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/d76337ec-a062-63fd-e411-5bf40add247b.png">

## claspのインストールとログイン
既にnpmをインストールしているものとして準備を進めていきます。
はじめに`clasp`をグローバルインストールしてください。

```bash:ターミナル
$ npm install -g @google/clasp
```

次にclaspコマンドからGoogleアカウントのリソースを扱えるように認証を通します。

```bash:ターミナル
$ clasp login
> Logging in globally...
> Authorization successful
> Default credentials saved to: ~/.clasprc.json
```
ブラウザが立ち上がるのでポチポチっと認証を許可します。
`.clasprc.json`ファイルの有無でログイン状態を判断しているようですね。

## claspプロジェクトの作成
好きな場所にワークディレクトリを作成して`npm init`してください。今回はTypeScriptで開発を行うため型の定義ファイルもインストールします。

```bash:
$ npm init -y
$ npm install -D @google/clasp
$ npm install -S @types/google-apps-script
```

続いて `clasp create` コマンドでclaspプロジェクトを作成します。今回は`gas-sunday`という名前でclaspプロジェクトを作成しました。

```bash:ターミナル
$ clasp create gas-sunday

? Create which script? (Use arrow keys)
❯ standalone 
  docs 
  sheets 
  slides 
  forms 
  webapp 
  api 
```

---
**:warning: 既存のGASからclaspプロジェクトを作成する時は`clasp clone スクリプトID`コマンドを使えばOKです :warning:**

---

コマンドを実行するとスクリプトの種類を問われますので必要に応じて選択しましょう。
`standalone`は他のアプリに依存しないスクリプト、`docs`や`sheets`はドキュメントやスプレッドシートに結びついたスクリプトです。アプリと結びつけると独自クラスが使えます。
基本的には`standalone`でほぼ対応できます。アプリベースのTriggerもコードでセットすれば良いので〜〜。

## 設定ファイルの編集、オンラインからpullする
`clasp create`コマンドでワークディレクトリ内に`.clasp.json`と`appsscript.json`が作成されました。

```bash:ターミナル
$ ls -a
.                       .clasp.json             node_modules            package.json
..                      appsscript.json         package-lock.json       tslint.json
```
名前の通り`.clasp.json`は claspプロジェクトの設定、 `appsscript.json`はGASの設定ファイルです。

`.clasp.json`に以下の設定を追加すると`clasp push`の対象ディレクトリを指定することができます。ついでにTypeScriptの設定も書いておきます。

```json:.clasp.json
{
  "scriptId":"##################################",
  "rootDir": "./src",
  "fileExtension": "ts"
}
```

設定した通りに`src`ディレクトリを作成しておきましょう。

```bash:ターミナル
$ mkdir src
```

ここで一度`clasp open`コマンドを叩いてオンラインにスクリプトが作成させていることを確認しておきます。

```bash:ターミナル
$ clasp open
```
コマンドを実行すると自動的にブラウザが開かれます。
<img width="1440" alt="スクリーンショット 2019-07-07 16.37.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/2833a4ff-3624-584d-6dde-737c3ad53473.png">
問題なくスクリプトが作成されているようです！
しかし、オンライン上の`Code.gs`ファイルはローカルにはありませんね。
そこで`clasp pull`コマンドを使います。

```bash:ターミナル
$ clasp pull
Cloned 2 files.
└─ ./src/appsscript.json
└─ ./src/Code.ts
```
これでローカルにオンライン(リモート)の`Code.js`と`appsscript.json`をsrc配下にpullすることができました。
今回はTypeScriptを指定しているため`Code.js`は`Code.ts`へと変換されています。

ここまでで環境がだいぶ整ってきました！

## コードを記述してpushする
ではローカルでGASを書いてclasp pushしてみます。
今回はとってもシンプルなTypeScript。

```ts:Code.ts
function myFunction() {
  var greeting: string = 'hello, GAS'
  Logger.log(greeting);

  var aisatsu: string = 'こんにちは、GAS'
  console.log(aisatsu);
}
```
`Logger.log`はオンラインエディタ上でログを出力させる命令です。
それではさっそくこのコードをclasp pushしてみましょう。

```bash:ターミナル
$ clasp push
```
ブラウザでスクリプトを確認すると.tsファイルが上手いこと`.gas`ファイルに変換されてpushされていることが分かります。
<img width="615" alt="スクリーンショット 2019-07-07 17.01.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/c5315d7f-db6b-af33-2096-6ff79858dc2c.png">

再生ボタンみたいなボタンを押してコードを実行してみるとログに`hello, GAS`と吐き出されています。
<img width="1020" alt="スクリーンショット 2019-07-07 17.04.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/1c7ea99a-15e5-2c21-caaa-9a4efe480f07.png">
`console.log`のログはSTACKDRIVERのLoggingに吐かれるためここには出ないです。

ここまででローカル環境での開発が可能になりました:sunny:

ここからはさらに一歩踏み込んでローカル環境でログの確認(デバッグ)を行えるようにしてみます。
GASでそこまでする必要あるのか?って内容ですので気になる人だけ見てください。


## ローカルからGASを叩く
「 ローカルでログの確認(デバッグ)ができる == GAS APIを叩いてローカルでログを取得できる 」ことです。
これを行うためにはGASをGCPプロジェクトへと関連付ける必要があります。

### GASを Cloud Platformプロジェクト と関連付ける
まずは[GCPのコンソール](https://console.cloud.google.com/home/)を開いて新しいプロジェクトを作成しましょう。
<img width="1129" alt="スクリーンショット 2019-07-07 17.29.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/f92c9ddc-8e8f-3aad-4a92-c481e6415283.png">
今回は`gas-sunday-gcp`というプロジェクトを作成しました。プロジェクトのダッシュボードから`プロジェクト番号`を確認しコピーしておきます。

次にGASのオンラインエディタに戻って **「リソース」 → 「Cloud Platform プロジェクト」**を開いてコピーしておいた`プロジェクト番号`をペーストして「プロジェクトを設定」をクリックします。
<img width="1081" alt="スクリーンショット 2019-07-07 17.37.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/20a95cf5-6d61-429f-02a1-0714861762f2.png">
さらに確認のモーダルを進めると`「プロジェクトに切り替えるには、OAuth 同意画面を設定する必要があります。 同意画面を設定するには、こちらをご覧ください。」`と怒られます。
<img width="899" alt="スクリーンショット 2019-07-07 17.39.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/a3838529-b985-3a91-32f5-7633b76b968f.png">
それでは怒られた通りに **こちら** をクリックしてOAuthの設定を行います。
OAuth同意画面で**「アプリケーション名」**を入力して**保存**します。(必要な場合はスコープの設定も行なって下さい。)

保存をクリックすると「API 認証情報」タブへとリダイレクトされます。
<img width="1420" alt="スクリーンショット 2019-07-07 17.46.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/f3945257-e940-7f2d-c1c8-d7cecd7e52f7.png">
次に**認証情報を作成 → OAuthクライアントID** と進みます。

進んだ画面で **デスクトップ アプリ** を選択し **作成** をクリックします。名前は何でも良いです。
クライアントIDが作成されるとモーダルで「クライアントID」と「クライアントシークレット」が表示されますが今回は特に気にする必要はありません。
<img width="1430" alt="スクリーンショット 2019-07-07 17.55.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/97ecdf36-cc9b-e29b-30e6-3f38ab1498cb.png">

ここでクライアントIDの一番右にあるダウンロードボタン(画像の赤丸)を押して`jsonファイル`をダウンロードします。
ダウンロードした`jsonファイル`は後ほど（claspプロジェクトのローカルログインで）使用します。ファイル名を`creds.json`に変更して保存しておきましょう。

これでOAuthの設定ができました。
再びGASのオンラインエディタに戻り、「プロジェクトの設定」を押すと今度は怒られることなくGASを Cloud Platformプロジェクト に関連付けることができます。
<img width="1063" alt="スクリーンショット 2019-07-07 18.02.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/43ba5d1d-bf37-acbc-2a68-e303e2e66404.png">

### GASを実行可能APIとして公開する
GASのオンラインエディタから **公開 → 実行可能APIとして導入**をクリックします。
<img width="787" alt="スクリーンショット 2019-07-07 18.14.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/17d70537-9f5c-c829-628d-3475f9e94b31.png">
スクリプトにアクセスできるユーザーは自由に選んで下さい。今回は「全員」に設定し、配置をクリックします。

次に、GCPのプロジェクトページから**APIとサービス → ライブラリ**へと進みます。
そこで`Apps Script API`を検索し、**有効にする**をクリックします。
<img width="1160" alt="スクリーンショット 2019-07-07 19.15.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/daf432e3-95b9-52d5-f6c5-b4e85a6fe9da.png">

これでスクリプトを実行可能APIとして公開できました。

### creds.json で localログイン する
ここからはコマンドラインからの操作に戻ります。オンライン(リモート)でAPI公開設定を行なったのでローカルでも設定を反映させるために`clasp pull`を実行しておきます。

```bash:ターミナル
$ clasp pull
```

`appsscript.json`に実行可能APIとして公開した設定が反映されているのが分かります。

```json:appsscript.json
{
  "timeZone": "America/New_York",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "executionApi": {
    "access": "ANYONE"
  }
}
```

次に`.clasp.json`にGCPのプロジェクトIDを設定しておきます。
以下のコマンドを実行しましょう。

```bash:ターミナル
$ clasp setting projectId <PROJECT_ID>
```
上記のコマンドで`.clasp.json`に`projectId`が追加されたことが確認できると思います。


続いてダウンロードしておいたjsonファイル`creds.json`をワークディレクトリ`gas-sunday`配下に入れます。`creds.json`には先ほど設定APIのクライアント情報が書かれてます。
このAPIクライアント情報を元に claspプロジェクトとしてワークディレクトリのlocalログイン を行います。

```bash:ターミナル
$ clasp login --creds creds.json
```
コマンドを実行すると自動的にブラウザに飛ばされます。

そしてボタンをポチポチして認証を進めます。

<img width="1089" alt="スクリーンショット 2019-07-07 18.44.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275194/b197aa48-0214-27d6-4234-6c21e3e19ba9.png">
警告を乗り越えて、許可ボタンを押していきます。

ターミナルに戻って以下のログが出れば完了です！

```
Authorization successful.
Local credentials saved to: ./.clasprc.json
```
globalログインしたときと似たように、今回はワークディレクトリ内にlocalログイン情報の入った`.clasprc.json`が作成されました。

### clasp run で関数を実行する
ここまでできれば準備万端です。
ローカル環境でスクリプトを実行するには`clasp run`コマンドを使います。

```bash:ターミナル
$ clasp run
```
コマンドを実行すると実行する関数を選択できます。


```
Running in dev mode.
? Select a functionName (Use arrow keys or type to search)
❯ myFunction 
```
初めから`clasp run 関数名`として実行することも可能です。

```bash:ターミナル
$ clasp run
? Select a functionName myFunction
No response.
```
`myFunction`を実行すると返り値が表示されます。今回は特に関数の帰り値がないため`No response.`となっています。

次にログを確認してみます。使うコマンドは`clasp logs`です。

```bash:ターミナル
$  clasp logs
DEBUG      2019-07-07T09:47:49.555Z myFunction      こんにちは、GAS   
```
実行された関数のログが吐き出されています。

おさらいですが、以下が今回実行した関数です。

```
function myFunction() {
    var greeting = 'hello, GAS';
    Logger.log(greeting);
    var aisatsu = 'こんにちは、GAS';
    console.log(aisatsu);
}
```
`console.log`で出力されたログが確認できます。なぜならSTACKDRIVERのLoggingのログを出力してるからです。
`Logger.log`のログは出ないので一応注意してください。

**これでGASをローカル環境で開発,デバッグすることが可能になりました :star:**


### トラブルシューティング
上手く `clasp run`が実行できなかった方は以下を確認してください。
#### パターン① `appsscript.json`に実行APIとしての公開設定が反映されていない

```json:appsscript.json
{
  "timeZone": "America/New_York",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "executionApi": {
    "access": "ANYONE"
  }
}
```
↓ ここがポイントです。

```json:
  "executionApi": {
    "access": "ANYONE"
  }
```
上記のような設定がない場合は**[GASを実行可能APIとして公開する](#gasを実行可能apiとして公開する)**をもう一度確認して下さい。

#### パターン② ローカルログインできない
`clasp login --creds creds.json`が上手くいかず、何故かglobalログインになってしまうパターン。
`creds.json`ファイルをワークディレクトリ配下に置いていることを確認して下さい。

`creds.json`をバッチリ配置しているのにlocalログインできない！という方。APIクライアントの有効化にラグがあることが原因かと思われます。1~6時間待って再度ログインを試してみて下さい。

#### パターン③ 公開可能APIのバージョンが最新ではない
**GASを実行可能APIとして公開する**を繰り返し行なった場合はAPIのバージョンが変わります。思いがけない挙動とならないようにGASのオンラインエディタからバージョンは最新のものを指定しておきましょう。


# おわりに
気軽な気持ちで書きはじめた記事でしたが、思ったより長くなり途中で後悔しました。
GASとTypeScriptの勉強頑張ります...!😭
それではまた！