# claspとGitHub ActionsでGoogle Workspaceアドオンを配信する

> gitプッシュからそのままApps Scriptへ — claspをインストールし、.clasp.jsonを設定し、あとはGitHub Actionsに任せる、ステップバイステップのガイド。

- Source: https://www.8apps.co/ja/blog/shipping-google-workspace-addon-clasp-github-actions
- Markdown: https://www.8apps.co/ja/blog/shipping-google-workspace-addon-clasp-github-actions.md
- Published: 2026-05-07
- Author: 8appsチーム
- Language: ja

---
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以上がインストールされていることを確認してから：

```bash
npm install -g @google/clasp
clasp --version
```

claspがあなたのアカウントと通信できるようにするには、Googleユーザーに対してApps Script APIを有効化する必要があります。[script.google.com/home/usersettings](https://script.google.com/home/usersettings) にアクセスし、**Google Apps Script API** を **オン** にしてください。これはアカウントごとに一度だけのスイッチです — 見落としがちで、忘れると `clasp` は分かりにくいエラーで落ちます。

ログインしましょう：

```bash
clasp login
```

ブラウザウィンドウが開き、OAuthフローを案内します。完了すると、claspは認証情報を `~/.clasprc.json` に保存します。CIをセットアップするときにこのファイルに戻ってきます。

---

## ステップ 2 — scriptIdを取得する

すべてのApps Scriptプロジェクトには一意な `scriptId` があります — claspはこれによって、どのプロジェクトにプッシュすべきかを判断します。取得方法は2通りあります。

**プロジェクトがまだ存在しない場合**、コマンドラインから作成します：

```bash
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プロジェクトにピン留めするファイルです。リポジトリのルートに置きます：

```json
// .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つのコマンドでデプロイできます：

```bash
clasp push -f
clasp deploy --description "first version"
```

`-f` は対話式の「マニフェストが変更されました」確認をスキップします — 常にオンにしておいて構いませんし、後のCIでは必須です。

---

## ステップ 5 — CIでclaspを認証する

ローカルのデプロイがうまくいくのは、`clasp login` が `~/.clasprc.json` に認証情報を残してくれるからです。GitHub Actionsのランナーにはそのファイルがなく、ブラウザフローも実行できないので、認証情報をシークレット経由で直接渡します。

ファイルの中身はおおよそこんな感じです：

```json
{
  "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に保存する手順：

1. ワークステーションで `clasp login` を実行（ステップ1で済んでいればスキップ）。
2. `cat ~/.clasprc.json` を実行し、出力をすべてコピー。
3. GitHub上のリポジトリで：**Settings → Secrets and variables → Actions → New repository secret**。
4. **Name:** `CLASPRC_JSON`、**Value:** いまコピーしたJSON全部。

> 💡 **ヒント：** このトークンは個人アカウントではなく、Apps Scriptプロジェクトを所有する専用のGoogleアカウントから生成してください。誰かがチームを離れてもローテーションは不要です。

---

## ステップ 6 — GitHub Actionsワークフロー

ここからが本番です。`.github/workflows/deploy.yml` にこれを置きます：

```yaml
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](mailto:support@8apps.co) までご連絡ください。
