Setting up CI/CD for Web-browser extension with GitHub Actions
Reupload of my posts from Blogspot from 3-Nov-20
Last time I've told you how to debug web extensions with VS Code. This time I going to show you some tools which help you to automate your extension development workflow. I'm talking about continuous integration and delivery
So, what CI/CD exactly is?
Continuous integration and delivery allow you to automate some parts of a development workflow like building, testing, packaging and distributing. So, everything you need here is to push your code into your repository and the rest will be done by computer
Cool, how can I opt-in?
Of course! All you need is to set up your CI/CD pipeline. But actually, it's not that simple if you try to do it yourself since there're a lot of underwater rocks. So that is why I'm doing this article
Today I'll cover set-ups of CI/CD pipelines for Chrome and Firefox extension webstores
Prerequisites
So, all you need right now is your WebExtension source code saved in GitHub repo. I will use for reference my latest and greatest Password generator extension. You can also check it out later to see how it is done there
Note that your project has to have at least one submission to selected stores to be automated
Workflow file setup
So, first thing we need to do is create our workflow file. These files contain instructions for computer on what and how it should integrate and deliver your extension. Usually, WFs have a .yaml
file extension. So, GitHub Actions WF is not an exclusion
Navigate to your repository root folder and create a .yaml
file on /.github/workflows/your_workflow_file_name.yaml
. Name of the file can be anything you want
So, here I've created a file on the exact folder path (you shouldn't push your file for now though)
Next thing, we need to fill our file with base instructions which we will use no matter where we will publish our project
name: CI # Name of your workflow on: workflow_dispatch: # Allows you to manually trigger workflow push: # Triggers workflow only if code is pushed to master/main branch branches: [ master, main ] paths: # Triggers workflow only if manifest content is changed - 'manifest.json' jobs:
Here we've set some triggers: conditions in which pipeline will be started
Here our triggers are set to fire only if we changed extension manifest on master/main branch. We did it because of several reasons:
- Here our triggers are set to fire only if we changed extension manifest on master/main branch. We did it because of several reasons:
- We don't want our changes from development branch be published as a major update
- Every time we submit new extension version, we have to change its version in manifest, so every push to master/main we want to publish has to contain this change
Note that YAML file syntax is indetation-sensitive. Make sure you didn't mess with tabs and spaces. More information about workflow file syntax you can find here
Now we need to make sure we have GitHub Actions turned on for our repository
To do that we need to navigate to Settings -> Actions
of the repo (https://github.com/%username%/%repository%/settings/actions
) and make sure we have checked anything but Disable Actions
Here ends general preparation of the workflow and now we will move to store-specific actions
Setting up Firefox webstore submission
Obtain API credential
Since we deal with some sensitive data here, we need to make sure everything is secured and nobody can sabotage our release process. So we need to obtain some access keys which will authorize our pipelines.
First thing, we need to go to Firefox Add-in API management page, generate and copy JWT issuer
value (that will be our client ID value) and JWT secret
(client secret)
Note: do not share these values with anyone else! Especially, DO NOT put these values into your workflow file as a plain text!
Navigate to the repository's secrets page (Settings -> Secrets
or https://github.com/%username%/%repository%/settings/secrets
) and create two new secrets:
FIREFOX_API_KEY
- Paste here yourJWT issuer
tokenFIREFOX_CLIENT_SECRET
- Put yourJWT secret
token here
You can use any other names for your secrets' variables
This is a safe storage for sensitive data. No one will be able to values stored here. Even you won't be able to see them
Update the workflow file
Now we need to add a new job to our workflow which will push our extension to the webstore
Open your workflow YAML file and add a new job:
jobs: Firefox: # Job name runs-on: ubuntu-latest # specify required OS for deployment machine steps: - uses: actions/checkout@v2 # Download your source code to the DM - name: Build Extension for Firefox id: web-ext-build uses: kewisch/action-web-ext@v1 with: cmd: build - name: 'Sign & publish' id: web-ext-sign uses: kewisch/action-web-ext@v1 with: cmd: sign channel: listed source: ${{ steps.web-ext-build.outputs.target }} apiKey: ${{ secrets.FIREFOX_API_KEY }} apiSecret: ${{ secrets.FIREFOX_CLIENT_SECRET }} - name: Drop artifacts uses: actions/upload-artifact@v2 with: name: 'Firefox Artefacts' path: ${{ steps.web-ext-sign.outputs.target }}
Here we have 4 tasks in our job:
- actions/checkout@v2 clones our source code to a deployment machine
- kewisch/action-web-ext@v1 with
cmd: build
packs our extension source code into XPI file which will be published to the webstore - kewisch/action-web-ext@v1 with
cmd: sign
signs the package (confirms that this package is legit and derived from official source) and publishes it to the webstore - actions/upload-artifact@v2 drops our XPI file to workflow artifacts and makes it available for us to download for sideloading and testing purposes (optional task)
In third sigining task we have to provide some parameters to publish our extension correctly:
cmd: sign
: The command to run. This command signs and submits our extension to Firefox servers for verificationsource
: path to our XPI package file. Here we use${{ steps.web-ext-build.outputs.target }}
since it is an environmental variablechannel
: Once verification is complete, our extension will become available for everybody in the webstore (listed
) or it will become available for self-distribution and won't become visible through the webstore (unlisted). Leave itchannel: listed
apiKey
: our JWT issuer token required for legit publishing. We can use here our secret variables -${{ secrets.VARIABLE_NAME }}
. In our case it's${{ secrets.FIREFOX_API_KEY }}
apiSecret
: JWT secret token
More info on workflow variables can be found on official GitHub documentation. If you click on links from actions list above, you can find out more about specific action and its documentation
Setting up Google Chrome webstore submission
Obtain API credential
For Chrome webstore a process of obtaining credential is a bit tricky and requires a lot of explanations. So, instead of making another article I just leave a link to a guideline which covers the case
All we need here is to obtain client ID
, client secret
and refresh token
. For more information what is it and how to live with it you cand find by googling an 'OAuth2' topic
Update the workflow file
Same as setting up Firefox workflow, we now need to add a new job to our pipeline:
jobs: Chrome: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Pack extension uses: TheDoctor0/zip-release@0.4.1 with: filename: ./Package.zip exclusions: '.git/* .vscode/* .github/* *.md' - name: Publish to Chrome Webstore uses: SebastienGllmt/chrome-addon@v3 with: extension: yourextensionidhere zip: ./Package.zip client-id: ${{ secrets.CHROME_CLIENT_ID }} client-secret: ${{ secrets.CHROME_CLIENT_SECRET }} refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }} - name: Drop artifacts uses: actions/upload-artifact@v2 with: name: 'Chrome Artifacts' path: ./Package.zip
As you can see, there're some major differences between Firefox and Chrome jobs. Here's some key points:
- We don't have to pack our extension with special format. Everything we need is to create a zip archive of our file with manifest in the root
- We don't have to sign our package. Everything will be done after submission
- We need to specify extension ID and provide a refresh token since we will publish our extension using "3d party" (technically, our) application
Everything else is pretty much the same as it is on the Firefox job, so I see no reason to stay on it anymore
More information about used actions you can find their marketplace pages:
- actions/checkout@v2
- TheDoctor0/zip-release@0.4.1
- SebastienGllmt/chrome-addon@v3
- actions/upload-artifact@v2
So, here's our complete workflow file:
So, that's it. All we have to do now is to push our updated workflow file to the repo and trigger our workflow and make sure everything is up and running
Wait, and how do I set up CI/CD for Safari/Microsoft Edge/etc. extensions?
You can't. Unfortunately, neither Apple nor Microsoft didn't make action tasks for automatic deployment of their browsers' extensions. And while it's understandable for the former one, since Safari has a different architecture of extensions and different submission processes, it's unclear for Microsoft not doing this, since new MS Edge is basically modified Google Chrome and it has same architecture and same submission processes as Chrome
Other browsers either aren't just popular enough or are based on different architecture
Conclusion
Hope this article will help you with your own project. If you still have any questions left, feel free to ask them in the comment section below. You can also leave a topic suggestion for my next article in the comment section as well
If you like ❤️ this, you can Buy Me a Coffee ☕ or follow me on Twitter 🗨. Thanks for your time ;)