GASに入門して,  Claspを使ったGit管理+Gitlab-CIを使ったCI / CDまでやった記録

# GASに入門して,  Claspを使ったGit管理+Gitlab-CIを使ったCDまでやってみた

　今冬、初めて雪が窓の外をちらついた日のことだ。
　GASを使ってスプレッドシートを更新する業務の自動化を推進している同僚が、保守性・メンテナンス性が悪くて困っているという話をしていた。
「バージョン管理をしたらいいよ」というアドバイスが方々から飛んだ。私もそのアドバイスをしたうちの一人だった。そのとき、私はGASを触ったことが一度もなかった。
　それから30分後、私はなぜかGASを触ることになっていた。何の偶然か、私はこのタイミングで突然**ドライブの特定のディレクトリに存在する全てのスプレッドシートからあるカラムを取得する必要性**に迫られたのだ。

## GASに入門する

　私はGASとは何かを検索したのちに、まずは適当なスプレッドシートを作成し、その上に初めてのGASを作成した。
　エディタは簡単に開いた。作成したスプレッドシート上でTools -> Script editorとクリックするだけだった。

　エディタ上には、`Code.gs`という謎の拡張子のファイルに空の関数が用意されていた。私はおもむろに入門サイトから**ハローワールド**をコピペする。

```js
function myFunction() {
  Logger.log('Hello, GAS')
}
```

　`Run`をクリックすると、画面下に実行ログが表示された。輝かしいこのコードこそ、私の初めてのGASコードである。
　これで「GASも触ったことがないだなんて」と後ろ指を指される人生とはおさらばだ。[^1]

[^1]: 実際には、私はGASを触ったことがないことで後ろ指を指されたことはありません。

　もう少し実用的な例を実装してみよう。スプレッドシートを新たに作成してみたい。
　調べたところ、スプレッドシートに値をまとめて入れるには、二次元配列が便利なようだ。
　何かちょうどいい二次元配列はないかと脳内にスキャンをかけると、引っかかったのは私のお気に入りの**アイドルグループ**だった。

```js
/**
 * @returns {string} spreadsheetId
 */
function generateFavoriteIdols() {
  const spreadSheet = SpreadsheetApp.create('faborite-idols')
  const idols = [
    // Lengths of all arrays have to match.
    ['Joshima', 'Kokubun', 'Matsuoka', 'Nagase', ''],     // TOKIO
    ['Ohno', 'Matsumoto', 'Aiba', 'Ninomiya', 'Sakurai'], // Arashi
  ]
  spreadSheet.getActiveSheet().getRange(1, 1, 2, 5).setValues(idols)
  return spreadSheet.getId()
}
```

　ついでに、生成したスプレッドシートからアイドルの名前を読み取る関数も書いてみよう。
　これができれば、私が業務で求められていた仕事はこなせることだろう。

```js
/**
 * @param {string} spreadSheetId
 * @returns {string[][]}
 */
function readFavoriteIdols(spreadSheetId) {
  const spreadsheet = SpreadsheetApp.openById(spreadSheetId)
  return spreadsheet.getActiveSheet().getRange(1, 1, 2, 5).getValues()
            .map(l => l.map(e => String(e)))
}
```

## Claspを使ってGit管理する

　仕事は済んだ。しかし私の胸には、一時間前に無責任にも放った「Gitでバージョン管理するといいよ」という自らの言葉がいつまでも刺さっていた。
　私はブラウザを開いて、「どうかいい感じの何かが出てくれ」というぼやっとした祈りと共に、検索欄に「gas git 管理」と入力した。

　無事にいくつかの検索結果を得た私は、**Clasp**を使って先ほど作ったコードをGit管理することにした。
　Claspのインストールと事前準備は下の記事を参考にした。

claspでGASのソースをGit管理
https://qiita.com/zaki-lknr/items/b4954c222c1c1db92caf

　さっそく先ほどまで触っていたスクリプトエディタのURLからIDっぽいところをコピペしてきて`clasp clone`してみると、無事コードをローカルに引っ張ってくることができた。

```sh
$ mkdir gas-sample && cd $_
gas-sample$ clasp clone ***
Warning: files in subfolder are not accounted for unless you set a '.claspignore' file.
Cloned 2 files.
└─ appsscript.json
└─ Code.js
Not ignored files:
└─ Code.js
└─ appsscript.json

Ignored files:
└─ .clasp.json
```

　`.gs`だった拡張子が、`.js`になっている。これは触れてやらないのが気遣いというやつだったかもしれない。

　いくつかのファイルを落とせたら、次はこれをgitで管理する。
　ユーザ名とメールアドレスは何でもいいが、自分のものを使うのが無難だ。

```sh
gas-sample$ git init
gas-sample$ git config user.email 'me@example.com'
gas-sample$ git config user.name 'me'
gas-sample$ git add appsscript.json Code.js
gas-sample$ git commit -m 'initial commit'
```

　ところで、`.clasp.json`の中を見たところ、私が先ほど`clasp clone`するときに使ったスクリプトIDが書いてあった。
　これはCDの中で設定した方が後々便利そうなので、ここではGit管理しないことにした。

```.clasp.json
{"scriptId":"***"}
```

　ここまででバージョン管理システムの導入が完了した。私の胸は歓喜に打ち震えている。
　バージョン管理システムを導入したからには、何か修正を入れてみたい。

　私はもう一つ**アイドルグループ**を追加することを思いついた。

```diff_javascript
   const idols = [
     // Lengths of all arrays have to match.
     ['Joshima', 'Kokubun', 'Matsuoka', 'Nagase', ''],     // TOKIO
     ['Ohno', 'Matsumoto', 'Aiba', 'Ninomiya', 'Sakurai'], // Arashi
+    ['', '', '', '', ''],                                 // SMAP
   ]
-  spreadSheet.getActiveSheet().getRange(1, 1, 2, 5).setValues(idols)
+  spreadSheet.getActiveSheet().getRange(1, 1, 3, 5).setValues(idols)
   return spreadSheet.getId()
```

```diff_javascript
   const spreadsheet = SpreadsheetApp.openById(spreadSheetId)
-  return spreadsheet.getActiveSheet().getRange(1, 1, 2, 5).getValues()
+  return spreadsheet.getActiveSheet().getRange(1, 1, 3, 5).getValues()
             .map(l => l.map(e => String(e)))
```

　できた。
　さっそくこの修正をGitに入れて、デプロイしてみよう。

```sh
gas-sample$ git add Code.js
gas-sample$ git commit -m 'Add SMAP to idols'
gas-sample$ clasp push
```

　はやる気持ちを抑えてブラウザでスクリプトエディタをリロードすると、無事修正が反映されていることが確認できた。

## Gitlab CIを使って自動でデプロイする

　人は、バージョン管理ができるようになったら、次は**CI / CD**を回したくなるものだ。
　私も一人の矮小な人間にすぎないということだろう。次の瞬間、私はGitlabを開き、新たにリポジトリを作成していた。[^2]

[^2]: Gitlab CIを採用したのは社内で使っているからというだけで、深い理由はありません。

　GitlabでClone with SSLの横に書かれたURLをコピーし、これをローカルのリポジトリで`origin`という名前に関連付ける。
　masterブランチをoriginのmasterブランチに対してpushすれば、Gitlabが私のコードを管理してくれるようになる。

```sh
gas-sample$ git remote add origin git@gitlab.com:me/gas-sample.git
gas-sample$ git push -u origin master
```

　ところで、Gitlabでは`.gitlab-ci.yml`というファイルをコミットすると自動的に**CI / CD**を回してくれるようになる。
　つまり、`.gitlab-ci.yml`というファイルを作り、そこに`clasp push`を実行するよう書くだけで、Gitlabに対して`git push`すると**GASが自動的に更新**されるようになるのだ。

　とりあえず`clasp push`するだけの`.gitlab-ci.yml`を書こう。
　認可情報とデプロイ先のスクリプトIDを環境変数へ逃がしたため、予めGitlabのリポジトリの設定 -> CI/CD -> Variablesに認可情報を`CLASPRC_JSON`, スクリプトIDを`SCRIPT_ID`として入力することが必要だ。[^3]

[^3]: 環境変数に直接入れてしまうと、悪意ある`.gitlab-ci.yml`をコミットされた場合に認可情報が流出する懸念があるので、公開リポジトリにpushする場合は工夫する必要があります。

```yml:.gitlab-ci.yml
stages:
- upload

upload:
  stage: upload
  image: node:10.23.2-alpine3.11
  script:
    # Never hard-code .clasprc.json, EVER
    - cp $CLASPRC_JSON ~/.clasprc.json
    - echo "{\"scriptId\":\"${SCRIPT_ID}\"}" > .clasp.json
    - npx @google/clasp push
```

　特に、**認可情報**(`~/.clasprc.json`)の**流出**には気を付けなければならない。これは決してGitで管理してはならない情報だ。`~/.clasprc.json`がログなど見えるところに残っていることに気がついたら、すぐにトークンを無効化しよう。

Apps with access to your account
https://myaccount.google.com/permissions?pli=1

　これで**CI / CD**を実現することができる。
　さっそくpushして、Gitlab上でパイプラインが流れるのを見てみよう。

```sh
gas-sample$ git add .gitlab-ci.yml
gas-sample$ git commit -m 'add .gitlab-ci.yml'
gas-sample$ git push
```

　Gitlab CIの実行ログには、"Job succeeded"の美しい文字が並ぶ。
　これでGitlabにpushするだけで全てが完結するようになった。

```sh
Executing "step_script" stage of the job script
00:22
$ cp $CLASPRC_JSON ~/.clasprc.json
$ echo "{\"scriptId\":\"${SCRIPT_ID}\"}" > .clasp.json
$ npx @google/clasp push
npx: installed 160 in 16.186s
└─ Code.js
└─ appsscript.json
Pushed 2 files.
Cleaning up file based variables
00:00
Job succeeded
```

　ここまでくれば、unittestの自動実行やAltJSを使った開発だって簡単だ。
　あの微妙に使い勝手の良いスクリプトエディタへの未練を捨て去り、普段使っているIntellij ideaやVSCodeで開発をしてもいいのだ。もちろんvimやemacsを使ってもいい。

　最後に、Gitlab-CIの環境変数のオーバーライドを利用して、別のスクリプトIDに対してデプロイを試してこの記事を終わりにしよう。
　これができれば、**開発環境・本番環境の切り替え**ができるだろう。

　新たにスプレッドシートを作り、Tools -> Script editorをクリックする。URLのIDっぽいところを控えておく。
　Gitlabに行き、ロケットのアイコンからPipelinesを開いて、`Run Pipeline`をクリックする。
　Variablesに`SCRIPT_ID`をvariable key, 控えておいたScriptIdをvariable valueとして、`Run Pipeline`をクリックする。
　緑色のチェックとpassedという文字を深い満足をもって確認する。

　スクリプトエディタをリロードすると、真っ白だったエディタの中には、先ほど作成したお気に入りの**アイドルグループ**表を作成するスクリプトが表示されていた。