Build Chrome extensions quicker with a TypeScript boilerplate project
Download

Automate Chrome extension publishing to Chrome Web Store

ExtensionNinja | 3/11/2023

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:

  1. Setup NodeJS
  2. Install NPM packages
  3. Call standard NodeJS build command “npm run build”
  4. Zip files in out folder (your output folder may differ)
  5. 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:

  1. Detect new release tag
  2. Run fresh build
  3. Download zip artifact from build action
  4. 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.

Don't miss latest Chrome extension developer news

Join Newsletter