GASのWebアプリケーションをGithubActions & Claspでデプロイする


はじめに

こんにちは!
最近なにかと話題のGAS.
実はWebアプリケーションとして公開できるのですが,
スプレッドシートを簡易的なデータベースとして使えたり,環境変数も設定できて簡易的なアプリケーションでやりたいことは一通りできます!(自分は社内でGAS製Slackアプリをいくつか運用してます.)
そしてなんといっても無料なところが素晴らしいです!

今回は,そんなGAS製WebアプリをGithubへのpushをトリガーにデプロイする方法を紹介します!

解決できるお悩み

  • GASのスクリプトエディタがリッチでないから,ローカルで好きなエディタで作業して,編集後にブラウザ上のGASエディタにコピペしてるけど,めんどくさい
  • Webアプリをデプロイするにいろいろ画面をぽちぽちしないといけないのがめんどくさい

今回構築するシステムの構成図

使うもの

  • Github Actions
  • Clasp : Google製のGAS用コマンドラインツール!これがあって本当に助かった..

大まかな流れ

-- 準備
1. 手元でclaspの認証情報などを集める
2. 1で集めた情報をgithub secretsに設定する

-- ここからGithubActions
3. secretsの値を埋め込んで認証用jsonを作成・配置する
4. 3のJsonを元にclaspコマンドでdeployする!

手順 全部で6ステップ!

その1:Claspのインストール & ログイン

まずは今回大活躍するツールClaspを手元の環境にインストール.
https://github.com/google/clasp

npm install -g @google/clasp

インストールが完了したら,以下のコマンドを使ってログインしてください.この時使用するアカウントは,今回デプロイしたいGASを所有しているアカウントにしてください

clasp login

ログイン完了すると,ホームディレクトリに.clasprc.jsonが作成されます.このファイルに記載されている情報をあとで使います.

その2:Google Apps Script APIを有効にする

以下にアクセスしてGoogle Apps Script APIをONに設定します

https://script.google.com/home/usersettings

その3:作成済みのGASのコードを手元にcloneする

カレントディレクトリにGASのコードと設定ファイル(appsscript.jsonと.clasp.json)がcloneされるので,すでにレポジトリ管理している場合は適宜ディレクトリ移動してください.

clasp clone "スクリプトID"

スクリプトIDはGASエディタのURLの次の部分のことです

https://script.google.com/d/**ここ**/edit?mid=hoge&uiv=fuga

自分はsrcディレクトリ配下にコードをおいたのでこんな感じになりました.この時点で手元に元々あったGASコードは破棄してもいいかもしれません.手元の方が最新であればsrc/index.jsを上書きしましょう.

src
 └── appsscript.json
 └── .clasp.json
 └── index.js(このファイル名はGASエディタで設定されたもの)

その4:Github Secretsに必要な情報を登録していく.

ここでActionsでclaspを使うための準備をします.
secretsはレポジトリ のsettingsのSecretsタブから設定できます!

今回設定する必要のあるシークレットは全部7つです.
骨の折れる作業ですが,ここまできたらもう一息です!
以下がシークレットに保存したい内容です
1. アクセストークン
2. IDトークン
3. リフレッシュトークン
4. クライアントID
5. クライアントシークレット
6. スクリプトID
7. デプロイメントID

それぞれのActionsでの役割は以下です
1~5:clasp loginするため
6: clasp pushするため
7: clasp deploy --deploymentId $DEPLOYMENT_IDデプロイして最新バージョンを公開するため

1~5 をログイン時に生成された.clasprc.jsonから抜き出す

以下などでファイルの中身を確認してください.

vim ~/.clasprc.json

おそらく以下のようなjsonになっていると思うので該当する値を一個ずつシークレットに登録してください.
今回Actions用yamlのサンプルでは次のような名前で登録しているのでそれに合わせていただくと楽かもしれません

.clasprc.jsonの中身


{
  "token": {
    "access_token": "アクセストークン",
    "scope": "hoge",
    "token_type": "Bearer",
    "id_token": "IDトークン",
    "expiry_date": 1606063959625,
    "refresh_token": "リフレッシュトークン"
  },
  "oauth2ClientSettings": {
    "clientId": "クライアント",
    "clientSecret": "クラインアントシークレット",
    "redirectUri": "http://localhost"
  },
  "isLocalCreds": false
}

サンプルyamlで使うシークレットの名前
secrets.HOGEのHOGEの部分

    env:
      ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
      ID_TOKEN: ${{ secrets.ID_TOKEN }}
      REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }}
      CLIENT_ID: ${{ secrets.CLIENT_ID }}
      CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
      SCRIPT_ID: ${{ secrets.SCRIPT_ID }}
      DEPLOYMENT_ID: ${{ secrets.DEPLOYMENT_ID }}

6 をclone時に生成された.clasp.jsonから抜き出す

こちらも以下のようなjson担っていると思うので値をシークレットに保存してください.サンプルではSCRIPT_IDで保存してます.

{"scriptId":"スクリプトID"}

7をGASエディタ上から取得する

デプロイメントIDはGASをWebアプリケーションとして公開した際のURLから取得できます.
GASエディタのメニューで,
公開 -> ウェブアプリケーションとして公開
を選択すると次のダイアログが出現すると思います.
赤枠部分のURLが公開URLです.

デプロイメントIDはこのURLのうちの以下の部分です

https://script.google.com/macros/s/**ここ**/exec

これもシークレットに保存してください
サンプルではDEPLOYMENT_IDで保存してます.

これでシークレット登録作業は終わりです.お疲れ様でした.

その5:Github Actionsで使用するshellscriptを追加する

いよいよ大詰めです.
Actionsの中で先ほどのシークレットの情報をかき集めて再びjsonファイルにしてあげる必要があります.
そのために以下のshellscriptを適当な位置に配置してください.
サンプルではsrcと同じ階層にdeploymentディレクトリをつくり,その中に配置しています

.clasprc.json生成スクリプト

setup.sh

#!/bin/sh

LOGIN=$(cat <<-END
    {
        "token": {
            "access_token": "$ACCESS_TOKEN",
            "refresh_token": "$REFRESH_TOKEN",
            "scope": "https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/service.management https://www.googleapis.com/auth/script.deployments https://www.googleapis.com/auth/logging.read https://www.googleapis.com/auth/script.webapp.deploy https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/script.projects https://www.googleapis.com/auth/drive.metadata.readonly",
            "token_type": "Bearer",
            "id_token": "$ID_TOKEN",
            "expiry_date": 1595752666211
        },
        "oauth2ClientSettings": {
            "clientId": "$CLIENT_ID",
            "clientSecret": "$CLIENT_SECRET",
            "redirectUri": "http://localhost"
        },
        "isLocalCreds": false
    }
END
)

echo $LOGIN > ~/.clasprc.json

.clasp.json生成スクリプト

setup_claspjson.sh

#!/bin/sh

CLASPJSON=$(cat <<-END
    {
        "scriptId": "$SCRIPT_ID"
    }
END
)

echo $CLASPJSON > ~/.clasp.json

ここまできたらもう終わったも同然です!
最後にGithub Actionsの設定ファイルを書きます!

その6:Github Actions用yamlを追加する

レポジトリに.github/workflowsを作成し
その中に以下のdeploy.yamlを配置してください


name: Deploy

on:
  push:
   #branches: 自分は本番環境用yamlにはこちらを追加して,masterマージのみで反応するようにしてます.
    # - master
    paths:
      - 'src/index.js' #pushを検知したいパスを書く

jobs:
  deploy:
    runs-on: ubuntu-latest

    env:
      ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
      ID_TOKEN: ${{ secrets.ID_TOKEN }}
      REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }}
      CLIENT_ID: ${{ secrets.CLIENT_ID }}
      CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
      SCRIPT_ID: ${{ secrets.SCRIPT_ID }}
      DEPLOYMENT_ID: ${{ secrets.DEPLOYMENT_ID }}

    steps:
      - name: Setup repository
        uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: '12'

      - name: Install Clasp
        run: npm install -g @google/clasp

      - name: Generate ~/.clasprc.json
        run: |
          bash deployment/setup.sh
      - name: Generate ~/.clasp.json
        run: |
          bash deployment/setup_claspjson.sh
      - name: Push
        run: |
          cd src
          clasp push --force
      - name: DEPLOY
        run: |
          cd src
          clasp deploy --deploymentId $DEPLOYMENT_ID

お疲れ様でした!以上で作業は終わりです!
あとは上のファイルで指定したパスに変更を加えてpushすれば
デプロイされるはずです!

最後に

運用上の注意

ステージングと本番アプリを分けて運用する場合,
異なるコードをそれぞれのアプリにデプロイしたいと思います.
その場合,GASアプリごとにSCRIPT_IDとDEPLOYMENT_IDが異なるので,シークレットのうちこの2つをアプリケーションごとに用意してください.残りはGmailアカウントが同じなら共有できます!

以下は例えば本番環境向けのyamlを書く場合は以下のように別シークレットを用意してください

SCRIPT_ID: ${{ secrets.PROD_SCRIPT_ID }}
DEPLOYMENT_ID: ${{ secrets.PROD_DEPLOYMENT_ID }}

clasp deploy コマンドの注意

上記のサンプルyamlを見てもらうと分かるようにdeployコマンドは以下のようにoptionをつけてないと最新のアプリケーションが公開されません.

clasp deploy --deploymentId $DEPLOYMENT_ID

clasp deployは新しいバージョンを作るだけで公開はしてくれないのです.
自分は頭のなかでdepoy==公開だと思っていたのでここでしばらくハマってしまいました.
違いまとめ

clasp push: コードをウェブのGASエディタに反映する(バージョンを作るとは言ってない)
clasp deploy:新しいバージョンを作る(公開するとは言ってない)
clasp deploy --deploymentId $DEPLOYMENT_ID:最新バージョンを公開する

以上で本当に最後です.長文お付き合いありがとうございました!

参考