1年半前に同様のことに挑戦したのですが、その後より良いやり方にたどり着いたのでアップデートの記事になります。
前回記事からの差分
- gapps → clasp を使うように変更した
- 秘匿したい情報は全て環境変数で渡せるようにして、publicリポジトリでも使えるようになった
- webpackでtypescriptをビルドして、生成物をデプロイするようにした
- 旧プロジェクトのソースコードをtypescriptに移植した
やりたいこと
- Google Apps Scriptのソースコードをgit管理したい
- それらのコードをCI経由でGoogle Apps Scriptにデプロイしたい
- コードはtypescriptで書きたい
どうやったか
claspを使うと手元のファイルをGoogle Apps Scriptにアップロードできるようになるので、同じことをCI上で行うようにします
CIは何でも良いですが、今回はcircleCIを使いました
実際に試してみたのがこちらのリポジトリです
https://github.com/abeyuya/gas-slack-bot-aoba
細かい設定などはリポジトリを直接見てもらうと早いと思います
CIからのデプロイ
claspではgappsと同様に認証情報をローカルのjsonファイルに保持しており、それが無いとデプロイなどはできません
(軽く調べた感じだと、環境変数で認証情報を渡せるような機能はなかったと思います)
今回は環境変数にjsonファイルをそのまま持たせて、デプロイフローの中でその環境変数をjsonファイルに書き出すようにしました
$ export CLASPRC='{"access_token":"xxx","token_type":"Bearer","refresh_token":"xxx","expiry_date":1500000000000}'
$ export CLASPCONF='{"scriptId":"xxx", "rootDir":"dist"}'
# !/bin/bash
echo $CLASPRC > ~/.clasprc.json;
echo $CLASPCONF > ./.clasp.json;
webpack + gasのハマりポイント
今回はローカルでtypescriptで開発し、webpackでビルドしたものをデプロイするようにしました
ここでいろいろと苦労したので、そのポイントを書いておきます
node_modulesが(ほぼ)使えない
これが一番苦労しました
gasは基本的にはgasで提供されているライブラリや組み込みモジュールを使って開発します
今回も UrlFetchApp
や OAuth1
などを利用しました
しかしこれらを使うとローカルでの動作確認などができなくなるため、極力nodeのライブラリを使ってやれないかなーと思って試行錯誤していました
結論的にはnodeのライブラリはほぼ使えないということがわかり、諦めて組み込みモジュールを使うようにしました
下記に試行錯誤のときの記憶をメモっておきます
- 成果物をclasp pushしようとしたらエラー
- syntax errorとかが出ている
- arrow functionが使えないっぽい
- ES6対応してないのか?
- ぐぐったらgasはES5をベースにしているらしい
- 他にもいろいろエラー出てた
- webpackの設定をいじって解決
- 最終的には
libraryTarget: 'this'
にした
- 最終的には
- 成果物をgas上で実行したらエラー
- requireが使えないんだけど、使おうとしてエラーとか
- nodeの組み込みモジュールを使おうとしていた
- gas上にはそんなものない
- 組み込みモジュールのpolyfillとか代わりのライブラリ入れたら行けるのでは
- 入れてもエラー
- Uint8Arrayがgasだと無いとかそういう感じ
- 使うライブラリによるがdevelopment modeでビルドすると4万行くらいあったので、これがgasでちゃんと動くのかちょっと不安
上記のような苦労を経て、諦めて組み込みモジュールを使うことにしました...
使えそうなライブラリも合ったのですが、上記のような制約の無い(gasに存在しない機能を使っていない)ライブラリだけなのかなーという気がしています
現実的にそういう選定はできないし、大規模なライブラリだとまずこの制約に引っかかる気がするので、ライブラリはgasでは使えないのでは、という結論に達しました
私のwebpack力が足りないだけという可能性も感じていて、上手く設定すればgasで動くようにビルドできるような気もちょっとします
どなたか知見があれば教えて頂けるとめっちゃ嬉しいです
webpackに buildTarget: 'gas'
みたいなオプションがあれば最高だったなぁ...
doPost
が呼ばれない
- [Webアプリケーションとして導入]はちゃんとやっている
-
curl -X POST {url}
とするとエラーが帰ってくる - エラーメッセージ詳細メモるの忘れたけど、「認証が必要です」的なエラー
- GSuiteアカウントのGASだと管理者かだれかの認証をしないとダメらしい
- 個人のGoogleアカウントに切り替えたらOKだった
感想
前回の感想と同じ結論になったのですが、本格的にアプリケーション開発するならやはりgasは向かないと思いました
今回のような環境を構築すればローカルで書けて良いじゃんと思ったのですが、結局gas上のモジュールに依存するのでローカルで開発するのはまあまあ大変です
今回もローカルでコード書く→ビルド&デプロイ→gasで動作確認というフローで開発をしていました
ローカルで開発できないと複数人での開発が大変になり、gasでの大規模なアプリケーションの開発は辛いと感じました
ローカルでやるならgasの機能をmockするようなテストコードを書きながらになるかなと思いましたが、面倒そう
今回作っていたのはslack botで別にgasである必要性はないので、もしがっつり開発することになるならserverless frameworkでFaaSで運用した方が楽かなーと思っています
実際、今回slack botの移植をしていた時にserverless framework + FaaSにしようかなとちょっと考えていました
しかし、google spread sheetとかと連携したアプリを書こうとするとやはりgasで書けた方がいろいろ楽な面もあり、開発の規模感とか用途によって使い分けるという感じになるのかなと思います
私の会社では結構google spread sheetをヘビーに使っており、それに伴ってgasのコードも結構動いています
そういうgasのコードは一度書いたらそれっきりとなる場合が多く、基本一人でサクッとスクリプトを書くという感じなので、そういう場合にgas以外でやろうとするとオーバーエンジニアリング感あるのでgasでいいじゃんという気持ちはあります
たまにこういうgasのコードをメンテすることがあるのですが、そういう場合にgit管理されていなかったり、実装者が不在だったり、デプロイどうやってやっていたのかわからなかったり、といった今後問題が起きそうだなと感じており、そういう問題に備えてgasをちゃんと運用できるしくみを作ってみようというのが今回の移植のモチベーションになりました
結論としては、小規模なアプリケーションだが「止まると困る」「たまに修正する」「長い間動かすことになりそう」みたいなgasのプロジェクトではこういうCI的な手法を採用する価値があるかなーと思いました
参考
Google Apps Script をローカル環境で快適に開発するためのテンプレートを作りました
今回やりたかったことに似た構成を簡単に作れるようです