Automating Chrome extension publishing with GitHub Actions

Automating Chrome extension publishing with GitHub Actions
Photo by John Schnobrich / Unsplash

In the earlier days of Jam engineering, we did a few things that don’t scale. As one of those many things, we manually submitted the extension to the Chrome Web Store. This meant whenever we wanted to release newer versions of our extension, one of our engineers had to run a script to build the extension bundle and literally click and drop the archive file into the Web Store dashboard.

Needless to say this became a pain point for us! This post describes our approach to automating this, but before that here’s a quick overview of how publishing an extension works:

  1. Build your Chrome extension
  2. Archive the output folder
  3. Go to Chrome Web Store Developer Dashboard
  4. Potentially login, and switch to your organization’s publisher
  5. Upload new package
  6. Submit for review
  7. Wait 1-2 business days for extension review
  8. Extension is published

To save us time, whenever we push a tag to our master branch, our continuous integration system automatically uploads the extension archive to the Web Store!

Automating extension upload

We use GitHub Workflows as our CI system and follow the GitHub branching model so our shared branches are develop , staging, and master.

When we push a semver git tag to master, CI runs a specific workflow for publishing the extension:

name: Update Chrome Extension

on:
  push:
    tags:
      - v*.*.*

Inside of this workflow we have two main jobs, building and publishing the artifact.

jobs:
  build-chrome-extension:
    name: Build Chrome extension artifact
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/[email protected]

      - name: Build
        run: |-
          # fill in your build steps here...
          # we archive the dist folder and include SHA commit as the last step
          zip -r chrome-extension-${{ github.event.pull_request.head.sha }}.zip dist

      - name: Archive chrome-extension artifact
        uses: actions/[email protected]
        with:
          name: chrome-extension-${{ github.sha }}
          path: chrome-extension-${{ github.event.pull_request.head.sha }}.zip

Then the next job is for uploading the extension. We use a CLI utility called chrome-webstore-upload-cli

  # api usage reference:
  # * <https://developer.chrome.com/docs/webstore/using_webstore_api/>
  # * <https://github.com/fregante/chrome-webstore-upload/blob/main/How%20to%20generate%20Google%20API%20keys.md>
  upload-extension:
    name: Upload extension
    runs-on: ubuntu-latest
    needs: build-chrome-extension
    env:
      # you can optionally specify extension ID here, we do this so that
      # all of our environments (dev, staging, prod) have a consistent ID
      # we can reference. Otherwise the extension ID is autogenerated.
      EXTENSION_ID: <extension id her> 

    steps:
      - uses: actions/[email protected]
        with:
          node-version: "16.10"

      - name: Download bundle artifact
        uses: actions/[email protected]
        with:
          name: chrome-extension-${{ github.sha }}

      - name: Install webstore cli
        run: |-
          npm install -g chrome-webstore-upload-cli

      - name: Upload step
        run: |-
          chrome-webstore-upload upload \\
            --source chrome-extension-${{ github.event.pull_request.head.sha }}.zip \\
            --extension-id ${{ env.EXTENSION_ID }} \\
            --client-id ${{ secrets.CI_GOOGLE_CLIENT_ID }} \\
            --client-secret ${{ secrets.CI_GOOGLE_CLIENT_SECRET }} \\
            --refresh-token ${{ secrets.CI_GOOGLE_REFRESH_TOKEN }}

You’ll need to generate Google API keys which is outlined here.

Then in your GitHub repo go to Settings > Security > Actions  and click New repository secret for each of the above secrets

Accessing the bundle

As a bonus, in the GitHub Workflow summary page, you can access the produced artifact at the bottom of the page.

In our other builds like staging branch or feature branches, we use the build artifact to leave a GitHub comment with a link to the bundle. You could also send a notification to your company chat like Slack, Hipchat, etc.

Chrome Web Store Submission

Once the above workflow gets triggered it only uploads the package. Remember that this is only staging the extension submission, we still manually visit the developer dashboard and submit for review but we’ve automated the most tedious parts of it.

Typically, you’ll update your manifest version for each release. We verify that the newest draft in the Developer Dashboard matches our upcoming version.

When we are ready to submit, we click Submit for Review

Then we wait! The approval process is async and takes 1-2 days but now that we’ve automated some of the toil, we can spend more time on building more features and making Jam better!