Automate Chrome extension publishing to Chrome Web Store
At some point everyone gets tired of building, zipping, uploading extension bundle to Chrome Web Store and then repeating everything again after noticing a small spelling mistake or a bug. Now replicate that over multiple extensions you may be maintaining, and we have a lot of unproductive time that could be spent on writing code.
If you are feeling this pain already, then good news, you are ready to invest a few minutes to automate your web extension publishing to Web Store. I will be using GitHub and GitHub Actions, but the same steps can be easily transferred to a different provider or even run locally.
At the end of this article, you will have a flow where you create a GitHub release tag, and the latest version of your extension automatically appears in Chrome Web Store ready for publishing.
Basics
I will assume that your code is already in a public or private GitHub repository. If you are not using a version control system, stop here and spend a few minutes reading about Git. Trust me, safely storing your code and having revision history is worth it even for a solo developer.
To automate the build process, you will need to create a GitHub workflow. We will do that in two steps.
First create a reusable build action in .github/actions/buildextension/action.yml directory.
My reusable build action looks like this:
- Setup NodeJS
- Install NPM packages
- Call standard NodeJS build command “npm run build”
- Zip files in out folder (your output folder may differ)
- Upload zip file as build artifact for later use or download
And this is how it looks in YAML form:
name: 'Build Chrome extension'
runs:
using: "composite"
steps:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install packages
run: npm ci
shell: bash
- name: Build extension
run: npm run build --if-present
shell: bash
- name: Pack extension
working-directory: out
run: zip -r extension-${{ github.event.pull_request.head.sha }}.zip *
shell: bash
- name: Upload extension archive
uses: actions/upload-artifact@v3
with:
name: extension-${{ github.sha }}
path: out/extension-${{ github.event.pull_request.head.sha }}.zip
Second, create a build workflow using the build action. Workflow definition goes to .github/workflows/build.yml file.
name: Build
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-extension:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/buildextension
Workflow is defined to run on each push or pull request to “main” branch. You should adjust this based on your branch management strategy.
At this point you should see a green status message in GitHub Actions pane next to your workflow. You should download the build artifact and inspect if the output matches what you were getting locally.
Upload to Web Store
We will now create an upload workflow that will take the zip file produced by the build action and upload it to Chrome Web Store. There is one complication. You will need to generate Google API client tokens and store them as GitHub repository secrets for the workflow to upload zip file on your behalf. There is already an excellent step by step guide on how to generate tokens. So, I will not repeat it. Come back once you are done.
Now that you have your client id, client secret and client refresh token we can complete the upload workflow. I will be using GitHub action that I created myself to communicate with Chrome Web Store API. You can use the same or choose from many others on GitHub marketplace.
Publishing workflow works like this:
- Detect new release tag
- Run fresh build
- Download zip artifact from build action
- Upload to Chrome Web Store
Create YAML file under .github/workflows/release.yml with content like this:
name: Upload to Web Store
on:
push:
tags:
- '*'
jobs:
build-extension:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/buildextension
upload-extension:
runs-on: ubuntu-latest
needs: build-extension
steps:
- name: Download extension archive
uses: actions/download-artifact@v3
with:
name: extension-${{ github.sha }}
- name: Upload to webstore
uses: ExtensionNinja/extension-publish@main
with:
action: upload
extensionID: INSERT_YOUR_EXTENSION_ID
clientID: ${{ secrets.GOOGLE_CLIENT_ID }}
clientSecret: ${{ secrets.GOOGLE_CLIENT_SECRET }}
clientRefreshToken: ${{ secrets.GOOGLE_REFRESH_TOKEN }}
extensionFile: extension-${{ github.event.pull_request.head.sha }}.zip
Create a new release tag in GitHub to test the workflow. Your workflow should be green, and you should see a new extension version in the Chrome Web Store.
Closing thoughts
I only maintain a few extensions, but development flow improvement has already been noticeable. I am loving the new publishing flow. I am no longer anxious about messing up something when building and publishing my extensions.
It did take a couple of tries to reproduce my build flow as a GitHub action and dealing with Google client tokens was a real pain. But it was forth it in the end.