Chrome拡張機能のリリースって面倒くさいですよね。Chrome Web Storeにアクセス → 拡張機能をzip化 → アップロードという作業を毎回行う必要があり、さらに以下も確認しなければなりません。
- アップロードするファイルは最新か
-
manifest.jsonのバージョンを更新したか - 正しいzipファイルを選択しているか
アップロードしてから「バージョンが既に使用されています」と怒られ、manifest.json を修正して再度zip化するのもかなり面倒です。決まった手順は自動化してしまいましょう。今回は、GitHub ActionsからWorkload Identity Federation(WIF)経由でChrome Web Storeへ拡張機能をリリースする方法を紹介します。調べてもこの方法がネットで出てこなかったので執筆しました。同じ方法で考えていた方は参考になると思います。
なぜWIFを使うのか
一応モチベを共有します。WIF経由でリリースする方法は他の文献でかなり紹介されています。具体的にはGitHub ActionsからGoogle Cloudへ認証する方法として、Service Account KeyのJSONをGitHub Secretsに保存する方法です。
ただし、長期間利用できる秘密鍵をGitHub側で管理する必要があり、流出時のリスクもあります。あと、複数の場所にデータを点在させるのってなんかカッコよくないですよね。
こういった背景から原則、環境変数をGoogle CLoudのSecret Managerに集約させて運用する開発スタイルをとっています。
そこで今回は Workload Identity Federation(WIF) を利用します。WIFを利用すると、GitHub Actionsが発行するOIDCトークンを使ってGoogle CloudのService Accountとして認証できます。つまり、Service Account KeyをGitHubに保存せずGoogle Cloudへ認証できます。
今回はこの仕組みを利用してChrome Web Store APIを呼び出します。
全体の流れ
- Google CloudでService Accountを作成する
- WIFを設定する
- Chrome Web StoreにService Accountを登録する
- GitHub ActionsのSecretsを設定する
- GitHub Actionsからビルド・アップロード・公開する
1. Google CloudでService Accountを作成する
Chrome Web Storeへのリリースに使用するService Accountを作成します。
例えば以下のような名前にします。
chrome-web-store-deployer
このService Accountを、GitHub ActionsからWIF経由で利用します。好みのアカウント名にしてください。
2. Workload Identity Federationを設定する
GitHub ActionsからService Accountとして認証できるよう、Google Cloud側でWIFを設定します。
主に以下を作成します。
- Workload Identity Pool
- Workload Identity Provider
- Service Accountとの紐付け
GitHub Actionsが発行したOIDCトークンをGoogle Cloud側で検証し、条件を満たしていればService Accountとして認証します。
3. Chrome Web StoreにService Accountを登録する
Webでサービスアカウントを登録する場所があるので作成したアカウントを登録しましょう。
4. GitHub ActionsのWorkflowを作成する
name: Deploy to Chrome Web Store
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build extension
run: pnpm zip
- name: Authenticate to Google Cloud
id: auth
uses: google-github-actions/auth@v3
with:
workload_identity_provider: projects/123456789/locations/global/workloadIdentityPools/github-actions/providers/github
service_account: chrome-web-store-deployer@example-project.iam.gserviceaccount.com
token_format: access_token
access_token_scopes: https://www.googleapis.com/auth/chromewebstore
- name: Upload to Chrome Web Store
run: |
curl -f -X POST \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
-T .output/*-chrome.zip \
"https://chromewebstore.googleapis.com/upload/v2/publishers/PUBLISHER_ID/items/EXTENSION_ID:upload"
- name: Publish to Chrome Web Store
run: |
curl -f -X POST \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
"https://chromewebstore.googleapis.com/v2/publishers/PUBLISHER_ID/items/EXTENSION_ID:publish"
上記のワークフローはこんな感じの手順を踏んでいます。とても綺麗ですね。
GitHub Actions
↓
ソースコードのビルド
↓
WIF経由で認証
↓
Chrome Web Storeへアップロード
↓
公開
補足: 拡張機能のバージョン更新も自動化する
Chrome Web Storeでは、前回と同じバージョンの拡張機能はアップロードできません。
そのため、リリース前に manifest.json 相当のバージョンを更新する必要があります。
今回はWXTを利用しているため、wxt.config.ts の version をGitHub Actionsから更新し、その変更をPull Requestとして作成します。
name: Bump Extension Version
on:
workflow_dispatch:
inputs:
bump_type:
description: "Version bump type"
required: true
type: choice
options:
- patch
- minor
- major
permissions:
contents: write
pull-requests: write
jobs:
bump:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Bump version
id: bump
env:
BUMP_TYPE: ${{ inputs.bump_type }}
run: |
set -euo pipefail
path="wxt.config.ts"
current_version=$(grep -Eo \
"version:[[:space:]]*['\"][0-9]+\.[0-9]+\.[0-9]+['\"]" \
"$path" \
| grep -Eo "[0-9]+\.[0-9]+\.[0-9]+")
IFS='.' read -r major minor patch <<< "$current_version"
case "$BUMP_TYPE" in
major)
major=$((major + 1))
minor=0
patch=0
;;
minor)
minor=$((minor + 1))
patch=0
;;
patch)
patch=$((patch + 1))
;;
esac
new_version="${major}.${minor}.${patch}"
sed -i -E \
"s/version:[[:space:]]*['\"][0-9]+\.[0-9]+\.[0-9]+['\"]/version: '${new_version}'/" \
"$path"
echo "current_version=${current_version}" >> "$GITHUB_OUTPUT"
echo "new_version=${new_version}" >> "$GITHUB_OUTPUT"
echo "branch=ver/extension-${new_version}" >> "$GITHUB_OUTPUT"
- name: Create Pull Request
env:
GH_TOKEN: ${{ github.token }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "${{ steps.bump.outputs.branch }}"
git add wxt.config.ts
git commit -m "chore: bump extension version to ${{ steps.bump.outputs.new_version }}"
git push origin "${{ steps.bump.outputs.branch }}"
gh pr create \
--title "chore: bump extension version to ${{ steps.bump.outputs.new_version }}" \
--body "Bump extension version from ${{ steps.bump.outputs.current_version }} to ${{ steps.bump.outputs.new_version }}." \
--base main \
--head "${{ steps.bump.outputs.branch }}"
Workflow実行時に patch / minor / major を選択すると、現在のバージョンから次のバージョンを計算します。
例えば、
1.2.3 + patch → 1.2.4
1.2.3 + minor → 1.3.0
1.2.3 + major → 2.0.0
となります。更新後は専用ブランチを作成し、バージョン変更のPull Requestまで自動で作成します。
バージョンを選択
↓
wxt.config.ts を更新
↓
commit / push
↓
Pull Request作成
PRをマージした後にChrome Web StoreへのデプロイWorkflowを実行することで、バージョン更新からリリースまでの手作業をかなり減らせます。今回はPRの作成までですが、プロダクトによってはpr作成後に自動でmerge+closeまでやっちゃっても良いかもしれないです。(これ相当便利ですね。なんだかんだこういう作業も2~3分かかるので)
おわりに
このワークフローを作成したことで、これまで煩わしかった手動アップロードから離れることが出来ました。古いソースコードをビルドして、リリースとかやらかしそうだったので初期段階で導入出来てよかったです。バージョンアップまで考えると、リリース毎に5分節約できるのと、ストレス軽減できます。是非参考にしてください。