Google Workspaceアドオンを開発しているなら、ランタイムが少し変わった場所にあることに気づくはずです。Apps ScriptはSSHできないサーバーであり、scpでファイルを送れないUIでもあります。すべての変更はGoogleのエディタかAPI経由で送る必要があります。あなたのリポジトリとその環境をつなぐ橋が clasp(Command Line Apps Script)です。これをGitHub Actionsに包んでしまえば、デプロイはただの git push になります。
この記事では、パイプライン全体をエンドツーエンドで歩きます:claspのインストール、リポジトリと特定のApps Scriptプロジェクトを結びつける設定、そして最後に main への各コミットを直接Apps Scriptへ送り込むGitHub Actionsワークフロー。
claspとは何か
clasp はGoogleが出している小さなNode CLIで、Apps Script APIと通信します。メンタルモデルはシンプルです:
- あなたのApps Scriptプロジェクトは
script.google.com/.../editのようなURLにあり、scriptIdで識別されます。 clasp pushはローカルディレクトリ内の.gs、.html、appsscript.jsonをスクリプトプロジェクトにアップロードし、サーバー側のコピーを置き換えます。clasp pullはその逆です — 誰かがApps ScriptのWebエディタでコードを編集し、その変更をgitに戻したいときに便利です。clasp deployはバージョン付きの名前付きデプロイメントを切り出します — アドオンストアやエンドユーザーが実際に動かすのはこれです。これを実行しないと、clasp pushは@HEADバージョンを更新するだけで、開発者以外の誰にも見えません。
ツールはこれだけです。ビルドシステムも、コードへの意見もありません — ディスク上のフォルダとGoogleのクラウド上のスクリプトプロジェクトを同期するだけの仕組みです。
ステップ 1 — claspをインストールする
clasp はグローバルなNodeパッケージです。Node 20以上がインストールされていることを確認してから:
npm install -g @google/clasp
clasp --version
claspがあなたのアカウントと通信できるようにするには、Googleユーザーに対してApps Script APIを有効化する必要があります。script.google.com/home/usersettings にアクセスし、Google Apps Script API を オン にしてください。これはアカウントごとに一度だけのスイッチです — 見落としがちで、忘れると clasp は分かりにくいエラーで落ちます。
ログインしましょう:
clasp login
ブラウザウィンドウが開き、OAuthフローを案内します。完了すると、claspは認証情報を ~/.clasprc.json に保存します。CIをセットアップするときにこのファイルに戻ってきます。
ステップ 2 — scriptIdを取得する
すべてのApps Scriptプロジェクトには一意な scriptId があります — claspはこれによって、どのプロジェクトにプッシュすべきかを判断します。取得方法は2通りあります。
プロジェクトがまだ存在しない場合、コマンドラインから作成します:
mkdir my-addon && cd my-addon
clasp create --type standalone --title "My Add-on"
--type には standalone、sheets、docs、slides, forms、webapp を指定できます。アドオンに合うものを選んでください。claspはGoogle側でプロジェクトを作成し、新しい scriptId がすでに記入された .clasp.json を書き出します。
プロジェクトがすでに存在する場合(Apps Scriptエディタで作ったか、誰かに共有された場合)は、script.google.com で開いてURLを見てください:
https://script.google.com/d/1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp/edit
└────────────────────── scriptId ──────────────────────┘
/d/ と /edit の間にある長い英数字部分があなたの scriptId です。Apps Scriptエディタの プロジェクト設定 → ID からも確認できます。
ステップ 3 — .clasp.json を設定する
.clasp.json は、ローカルディレクトリを特定のApps Scriptプロジェクトにピン留めするファイルです。リポジトリのルートに置きます:
// .clasp.json
{
"scriptId": "1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp",
"rootDir": "./src"
}
2つのフィールド、どちらも重要です:
scriptId— claspがプッシュする先のプロジェクト。ステップ2で取得したものを貼り付けます。rootDir— claspがプロジェクトのソースとして扱うローカルフォルダ。このフォルダ内のすべてがアップロードされ、それ以外は無視されます。rootDirを省略すると、claspはカレントディレクトリを使います — つまり通常はnode_modulesや.gitまでアップロードしようとしてしまいます。必ず明示的に設定してください。
たまに見かける任意フィールド:
scriptExtensionsとhtmlExtensionsは、claspがどの拡張子をサーバーコードまたはHTMLとして扱うかをカスタマイズします。filePushOrderは特定のファイルを他より先にプッシュするよう強制します —.gsファイル間にロード順の依存があるときだけ関係します。
ほとんどのプロジェクトでは、上記の2フィールド版で十分です。
ステップ 4 — ファイルを配置する
claspは rootDir の中にファイルがどう入ったかは気にしません。フォルダに有効なApps Scriptファイルが入っていれば、それをプッシュします。最小構成はこんな感じです:
my-addon/
.clasp.json
.claspignore // 任意 — プッシュ時にスキップするファイル
src/
appsscript.json // マニフェスト:スコープ、ランタイム、アドオン設定
Code.gs // サーバーサイド関数
Sidebar.html // HtmlServiceで配信するHTML
ファイルタイプについて押さえておくべき3点:
.gsファイルはApps Scriptのサーバーコードです。claspがエディタのネイティブ形式と透過的に変換します。.htmlファイルはHtmlService.createHtmlOutputFromFile()で配信されるテンプレートです。appsscript.jsonはプロジェクトのマニフェストです。OAuthスコープ、ランタイムバージョン(V8)、タイムゾーン、そしてアドオンの場合はユーザーに見えるメニューエントリやトリガーを宣言します。これがないとclaspはプッシュを拒否します。
rootDir の中にアップロードしたくないファイル(ビルドスクリプト、READMEなど、.gs/.html/appsscript.json 以外のもの)がある場合は、.clasp.json の隣に .claspignore を置きます:
**/**
!appsscript.json
!**/*.gs
!**/*.html
このパターンの意味は「すべてを無視し、その後Apps Scriptが実際に理解するファイル種別だけ無視を解除する」です。
レイアウトが整ったら、ローカルでは2つのコマンドでデプロイできます:
clasp push -f
clasp deploy --description "first version"
-f は対話式の「マニフェストが変更されました」確認をスキップします — 常にオンにしておいて構いませんし、後のCIでは必須です。
ステップ 5 — CIでclaspを認証する
ローカルのデプロイがうまくいくのは、clasp login が ~/.clasprc.json に認証情報を残してくれるからです。GitHub Actionsのランナーにはそのファイルがなく、ブラウザフローも実行できないので、認証情報をシークレット経由で直接渡します。
ファイルの中身はおおよそこんな感じです:
{
"token": {
"access_token": "ya29....",
"refresh_token": "1//0g....",
"scope": "https://www.googleapis.com/auth/script.projects ...",
"token_type": "Bearer",
"expiry_date": 1736000000000
},
"oauth2ClientSettings": {
"clientId": "xxxxxxxxxxxx.apps.googleusercontent.com",
"clientSecret": "GOCSPX-xxxxxxxxxxxx",
"redirectUri": "http://localhost"
},
"isLocalCreds": false
}
refresh_token が要です — 誰かが許可を取り消さない限り、claspは何ヶ月もこれを使って新しいアクセストークンを発行できます。
GitHubに保存する手順:
- ワークステーションで
clasp loginを実行(ステップ1で済んでいればスキップ)。 cat ~/.clasprc.jsonを実行し、出力をすべてコピー。- GitHub上のリポジトリで:Settings → Secrets and variables → Actions → New repository secret。
- Name:
CLASPRC_JSON、Value: いまコピーしたJSON全部。
💡 ヒント: このトークンは個人アカウントではなく、Apps Scriptプロジェクトを所有する専用のGoogleアカウントから生成してください。誰かがチームを離れてもローテーションは不要です。
ステップ 6 — GitHub Actionsワークフロー
ここからが本番です。.github/workflows/deploy.yml にこれを置きます:
name: Deploy Apps Script
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install clasp
run: npm i -g @google/clasp
- name: Restore clasp credentials
run: echo '${{ secrets.CLASPRC_JSON }}' > ~/.clasprc.json
- name: Push to Apps Script
run: clasp push -f
- name: Create a versioned deployment
run: |
DESC="ci-${GITHUB_SHA::7}-$(date -u +%Y%m%d%H%M)"
clasp deploy --description "$DESC"
各ステップが何をしているか:
on: push: branches: [main]—mainへのマージごとにデプロイがトリガーされます。workflow_dispatchはActionsタブに「Run workflow」ボタンを追加し、手動での再実行を可能にします。environment: production— GitHub Environmentにオプトインします。必須のレビュアーを設定すれば、すべてのデプロイは人間の承認を待ってから進みます。実ユーザーがいるアドオンには強くおすすめです。- Restore clasp credentials — シークレットを
~/.clasprc.jsonに書き戻し、claspが期待する場所で見つけられるようにします。シークレットを他の場所でcatやechoしないでください。GitHubは${{ secrets.* }}経由で使ったときだけ自動的にマスクします。 clasp push -f—rootDir内のすべてをApps Scriptプロジェクトにアップロードします。-fはCIでは必須で、これがないと「マニフェスト変更」プロンプトでランナーが固まります。clasp deploy --description— 新しいバージョン付きデプロイメントを切り出します。説明にはショートSHAとタイムスタンプを含めるので、どのデプロイメントもピンポイントのコミットまで辿れます。多くの人がここを忘れます — これがないとclasp pushは@HEADを更新するだけで、インストール済みのユーザーには永遠に届きません。
ワークフローをコミットしてプッシュし、緑のチェックを待つだけ。これ以降、デプロイはただの git merge です。
やらないこと(とその理由)
~/.clasprc.json をリポジトリにコミットしない。 中にあるリフレッシュトークンは事実上の長寿命クレデンシャルです。GitHub Secretsに置くべきもので、ソース管理に置くものではありません。
チームで1つのGoogleアカウントを共有しない。 CLASPRC_JSON シークレットは、Apps Scriptプロジェクトを所有する専用アカウントから生成します。個人アカウントは入れ替わっても、デプロイ用アカウントは変わりません。
このワークフローではテストを走らせない。 Apps ScriptのランタイムはNodeとは十分に異なるので、意味のあるユニットテストを書くのは難しいです。共有コードのNodeポートに対するロジックテストは別ワークフローで動かし、各デプロイ後の手動スモークテストに頼っています。
得られるもの
このセットアップ前のデプロイはこうでした:最新を取り込み、claspのトークンが切れていないことを祈り、clasp push を実行し、まともな説明付きで clasp deploy するのを忘れず、-f を付け忘れないことを祈る。5ステップ、5箇所のつまずきポイント。
いまは:main にマージし、緑のチェックを見届け、アドオンを開く。Apps Scriptはもう特別な存在ではありません — 他のすべてと同じgitワークフローの後ろにある、ただのデプロイ先です。
Workspaceアドオンを保守していて、いまだにラップトップからプッシュしているなら、移行は半日仕事です。一番難しいのは ~/.clasprc.json を見つけることです。
カスタムワークフローのアイデアがありますか?
あなたのビジネスプロセスにぴったり合うGoogle Workspaceアドオンをオーダーメイドで作ります。support@8apps.co までご連絡ください。