Are you ready to streamline your browser extension deployment process? Manually deploying extensions across multiple stores and managing complex manifest v3 requirements can be a daunting task. With GitHub Actions, you can automate the entire deployment process, ensuring consistency, reducing errors, and saving valuable time.
In this guide, we’ll walk you through how to automate deployments for Chrome Web Store, Microsoft Edge Add-ons, and Firefox Add-ons using GitHub Actions. Whether you’re deploying individual versions or compiling a release tag, our workflow will handle everything from checking out the latest code to packaging and publishing your extensions.
By the end of this article, you’ll be able to:
- Automate deployments across multiple stores
- Manage complex manifest requirements efficiently
- Set up version control integration for smooth deployment
- Take advantage of GitHub’s built-in features like release tracking
This guide is perfect for developers who want to ensure their extension deploys seamlessly and reliably. So, are you ready to take your extension deployment process to the next level? Let’s get started!
The Struggles of Multi-Browser Extension Deployment
Deploying a web extension to various browser web stores can be a laborious and error-prone task, particularly when working with manifest v3. Each platform imposes unique requirements that complicate the deployment process. Here are some common pain points:
- Firefox: Extensions utilizing bundlers like webpack or Extension.js require zipped source code. Additionally, Firefox mandates submissions in Cross-Platform Install (XPI) format.
-
Chrome and Edge expect submissions in
.zip
format without the need for source code.
In addition to these specific requirements, you must manually visit each web store and upload extension artifacts. To ease this burden, we can create a streamlined solution using GitHub Actions. This method will offer us numerous advantages:
- Automation: The entire deployment pipeline is automated, checking out the project, building it, and saving build artifacts in a GitHub release.
- Direct publishing: Artifacts are published directly to the Chrome Web Store, Mozilla Add-on Store, and Edge Partner Center.
- One-time setup: Once set up, you only need to cut a new release on GitHub to initiate the entire process.
- Signed artifacts: The action generates signed artifacts, attaches them to the release, and makes them available for users to download, inspect, or run locally without using a web store.
This guide will demonstrate gathering the necessary API keys and metadata, setting up each component of the pipeline, and automating the entire deployment process for your web extension.
Prerequisites
Before automating your web extension deployment, please ensure you have completed the following prerequisites:
- Have a Web Extension: You may choose to create a simple extension using various tools such as Extension.js or adapt an existing project that adheres to manifest v3 requirements. While this guide demonstrates using Extension.js, it is not strictly required for the deployment process.
-
Manually Publish the First Version: The initial publication process is essential for setting up your extension on each platform. Depending on the store, this process may take varying amounts of time:
- Chrome Web Store: Typically 1-3 days for review and approval.
- Mozilla Add-on Store: Usually 1-2 days.
- Microsoft Edge Add-ons: Initial reviews may take 4-7 days.
Once the initial build has been successfully published for review, proceed with gathering the required credentials to set up the automations.
Accumulating Credentials
To automate deploying your web extension to multiple browser web stores, we need API keys and some store-specific metadata for each store's APIs. Collect these credentials as follows:
-
Add them to your Repository Secrets as you go: You can find the Actions Secrets section in your GitHub repository settings under
github.com/<user>/<repo>/settings/secrets/actions
. Add all the collected keys and metadata to the Repository secrets under the Secrets tab.
Chrome Web Store Credentials
To publish on the Chrome Web Store, you’ll need:
- Extension ID: Available on your extension's product page. Open the Chrome Web Store, find your extension, and view its URL. The ID is the long string of characters at the end of the URL.
-
API Credentials: Set up through the Chrome Web Store API in Google Cloud. Follow these instructions to obtain:
CHROME_CLIENT_ID
CHROME_CLIENT_SECRET
-
CHROME_REFRESH_TOKEN
If you have trouble getting the Refresh Token, give it some time and try again. It can take an hour or so after setting up access to the Chrome Web Store API before it works in some cases.
Microsoft Edge Web Store Credentials
To automate publishing to the Microsoft Edge Add-ons store, use the Edge Add-ons REST API:
- Sign in to the Partner Center at partner.microsoft.com.
- Visit the Publish API page.
- Click Create API credentials to generate:
EDGE_CLIENT_ID
EDGE_CLIENT_SECRET
-
ACCESS_TOKEN_URL
Additionally, you’ll need the Product ID:
- Navigate to the Edge Overview page.
- Click on your extension to find the Product ID.
Firefox Add-on Store Credentials
For Firefox, use webext
, the preferred tool for automated deployments:
- Log in to addons.mozilla.org.
- Go to Tools > Manage API Keys.
- Agree to the terms and retrieve your Issuer ID and Secret:
FIREFOX_ISSUER
-
FIREFOX_SECRET
Once you are done collecting all of the necessary keys and metadata, your Repository Secrets should look similar to this:
Building the Github Actions Workflow
Now that we have accumulated all of the information we need to use our publishing APIs, it is time to build the workflow. First, create a new file in your repository's .github/workflows
directory with a name such as release.yml
.
Let's review the workflow plan before we get started.
### Workflow Summary
The workflow will be triggered upon Release of the project. This action implies that a tag and release have been created for a specific commit, followed by publishing the release.
Note: You may choose alternative methods to release your extension. If you do, ensure to skip the section regarding the automatic upload of build artifacts to the release after publication.
Advantages of this approach include:
- Automatically generated change logs for each release, based on GitHub's git history.
- Ability to keep certain versions of the extension available on the release page for future use, such as debugging older versions or providing transparency to users.
In detail, the workflow will perform the following tasks:
- Run upon a published GitHub release
- Set up a build environment
- Checkout code and install dependencies
- Create builds for each target browser
- Publish builds to their respective webstores
- Upload build artifacts to the latest GitHub release (if applicable)
Configure the Workflow to Run on a Published Release
Let's get started by giving our workflow a name and a hook.
# Used by the Github Actions runner to describe the workflow
name: Upload Release Build
# Configure the workflow to run when a new release has been published
on:
release:
types: [published]
Setup Build Environment
To ensure a smooth build process, let's set up our environment and run the builds for each browser:
# Define a job named "release" that runs on an Ubuntu image
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
# Checkout the latest code from the repository
- name: Checkout code
uses: actions/checkout@v4
# Install Node.js
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
# Create a directory for the builds
- name: Create directory
run: cd ..; mkdir ./builds
# Zip the source code (only needed if publishing to Firefox)
- name: Zip Source Code
run: zip -r ../builds/Source.zip .
# Install project dependencies
- name: Install Dependencies
run: npm ci
Run Targeted Builds
Depending on the bundler or framework being used, production builds will be triggered via your package file scripts. Since this example utilizes Extension.js, I've included the following build scripts:
{
"scripts": {
"build:ci:chrome": "extension build --browser chrome --polyfill",
"build:ci:firefox": "extension build --browser firefox --polyfill",
"build:ci:edge": "extension build --browser edge --polyfill"
}
}
Upload Bundles and Publish
Here's where the magic happens! We'll run through each target browser, automatically deploying to their respective webstores that have been configured.
Automate Release to the Firefox Add-on Store
This section will sign, upload, and publish our extension artifacts to the Firefox Add-on Store. You'll need to be sure to have web-ext as a devDependency
in your package file. You can install it with npm install -D web-ext
.
# Sign & Upload Firefox artifacts
- name: Sign and Publish artifact
continue-on-error: true
env:
FIREFOX_ISSUER: ${{ secrets.FIREFOX_ISSUER }}
FIREFOX_SECRET: ${{ secrets.FIREFOX_SECRET }}
run: npx web-ext sign --channel listed -s ./dist/firefox/ --upload-source-code ../builds/Source.zip --artifacts-dir ./dist/firefox --api-key $FIREFOX_ISSUER --api-secret $FIREFOX_SECRET
# Move the artifact to a more appropriate name
- name: Move artifact
run: mv ./dist/firefox/<your-app>-*.xpi ./builds/FirefoxExtension.xpi
# Upload the Firefox extension to the release
- name: Upload FirefoxExtension to release
uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
with:
name: FirefoxExtension.xpi
path: ./builds/FirefoxExtension.xpi
repo-token: ${{ secrets.GITHUB_TOKEN }}
Automate Release to the Chrome WebStore
This section will package, upload, and publish our extension artifacts to the Google Chrome Web Store
# Zip the Chrome artifacts
- name: Zip Chrome Artifacts
run: cd ./dist/chrome ; zip -r ../../builds/ChromeExtension.zip *
# Upload the Chrome extension to the release
- name: Upload ChromeExtension to release
uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
with:
name: ChromeExtension.zip
path: ./builds/ChromeExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Publish the extension to the Chrome Web Store
- name: Publish to Chrome Web Store
continue-on-error: true
uses: wdzeng/chrome-extension@v1
with:
client-id: ${{ secrets.CHROME_CLIENT_ID }}
client-secret: ${{ secrets.CHROME_CLIENT_SECRET }}
refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }}
zip-path: './builds/ChromeExtension.zip'
extension-id: <your-chrome-extension-id>
Automate Release to the Edge Add-on Store
This section will package, upload, and publish our extension artifacts to the Microsoft Edge Add-on Store
# Upload Edge artifacts
- name: Zip Edge Artifacts
run: cd ./dist/edge ; zip -r ../../builds/EdgeExtension.zip *
# Upload the Edge extension to the release
- name: Upload EdgeExtension to release
uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
with:
name: EdgeExtension.zip
path: ./builds/EdgeExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Publish the extension to the Edge Addon Store
- name: Publish to Edge Addon Store
continue-on-error: true
uses: wdzeng/edge-addon@v2
with:
product-id: <your-edge-addon-id>
zip-path: ./builds/EdgeExtension.zip
api-key: ${{ secrets.EDGE_CLIENT_SECRET }}
client-id: ${{ secrets.EDGE_CLIENT_ID }}
Test the Workflow
Testing your new GitHub Actions workflow is essential to ensure it functions as intended and avoids any issues upon deployment. Here's an easy-to-follow guide on testing your workflows locally before pushing them to the main repository:
Create a Test Branch: Start by creating a separate branch, ideally named
workflow-test
, to isolate any changes related to the workflow from your production code. This ensures that any potential issues will not impact your live application.Add your Modifications: Commit the new
release.yml
file to your test branch and push it to the GitHub remote branch.Enable Local Runs: Navigate to your repository's settings page, select
Code
in the left-hand menu, click onActions
, then click onGeneral
. Scroll down to the "Local environment" section, and enable "Allow local runs". This enables you to run your workflow locally.Run Workflow Locally: You can now execute the workflow locally by using GitHub CLI or running the workflow file in your local GitHub Actions runner.
Validate Test Results: Review the output from the locally executed workflow to ensure it behaves as intended. Address any issues or errors that may have arisen during testing before merging your changes into the main branch.
Remember to be patient! I almost always run into small configuration issues the first few times I write a new pipeline. If you run into any issues, feel free to mention me on GitHub and I'll try to help in any way I can.
If you still need some more help, you can checkout a simple working example from the LinkedZen extension on my GitHub.
Wrapping Up
Once everything is configured, your pipeline should look something like this:
name: Upload Release Build
on:
release:
types: [published]
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
# Checkout the code
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Create directory
run: cd ..; mkdir ./builds
# Zip the source code
- name: Zip Source
run: zip -r ../builds/Source.zip *
# Install dependencies
- name: Install dependencies
run: npm ci
# Create build directory
- name: Create build directory
run: mkdir ./builds
# Build for Firefox, Chrome and Edge
- name: Build for Firefox
run: npm run build:ci:firefox
- name: Build for Chrome
run: npm run build:ci:chrome
- name: Build for Edge
run: npm run build:ci:edge
# Sign & Upload Firefox artifacts
- name: Sign and Publish artifact
continue-on-error: true
env:
FIREFOX_ISSUER: ${{ secrets.FIREFOX_ISSUER }}
FIREFOX_SECRET: ${{ secrets.FIREFOX_SECRET }}
run: npx web-ext sign --channel listed -s ./dist/firefox/ --upload-source-code ../builds/Source.zip --artifacts-dir ./dist/firefox --api-key $FIREFOX_ISSUER --api-secret $FIREFOX_SECRET
- name: Move artifact
run: mv ./dist/firefox/<your-extension>-*.xpi ./builds/FirefoxExtension.xpi
- name: Upload FirefoxExtension to release
uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
with:
name: FirefoxExtension.xpi
path: ./builds/FirefoxExtension.xpi
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Zip Chrome Artifacts
run: cd ./dist/chrome ; zip -r ../../builds/ChromeExtension.zip *
- name: Upload ChromeExtension to release
uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
with:
name: ChromeExtension.zip
path: ./builds/ChromeExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to Chrome Web Store
continue-on-error: true
uses: wdzeng/chrome-extension@v1
with:
client-id: ${{ secrets.CHROME_CLIENT_ID }}
client-secret: ${{ secrets.CHROME_CLIENT_SECRET }}
refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }}
zip-path: ./builds/ChromeExtension.zip
extension-id: <your chrome extensions id>
# Upload Edge artifacts
- name: Zip Edge Artifacts
run: cd ./dist/edge ; zip -r ../../builds/EdgeExtension.zip *
- name: Upload EdgeExtension to release
uses: Shopify/upload-to-release@07611424e04f1475ddf550e1c0dd650b867d5467
with:
name: EdgeExtension.zip
path: ./builds/EdgeExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to Edge Addon Store
continue-on-error: true
uses: wdzeng/edge-addon@v2
with:
product-id: <your edge product id>
zip-path: ./builds/EdgeExtension.zip
api-key: ${{ secrets.EDGE_CLIENT_SECRET }}
client-id: ${{ secrets.EDGE_CLIENT_ID }}
Top comments (0)