avi12/web-ext-deploy
A tool for deploying WebExtensions to multiple stores.
WebExt Deploy
The ultimate automation tool for deploying to multiple extension stores simultaneously!
Made by Avi
Supported stores:
Core packages/APIs used
- Playwright - for fetching Opera credentials
- Chrome Web Store API v2
- Microsoft Edge Publish API v1.1
- Firefox Add-ons Store Submission API
- Opera Store API
Installing
npm i -D web-ext-deploy
# or
pnpm i -D web-ext-deploy
# or
bun add -D web-ext-deployor install globally
npm i -g web-ext-deploy
# or
pnpm i -g web-ext-deploy
# or
bun add -g web-ext-deployDeployment to Chrome Web Store: follow this guide
Deployment to Edge Add-ons Store: follow this guide
Usage
1. Get the relevant credentials for each store:
Disclaimer: I do NOT take any responsibility for leaked cookies or credentials.
- Opera:
sessionid,csrftoken— obtained via Playwright (use--auto-fetch-credentialsto fetch automatically) - Chrome:
REFRESH_TOKEN— obtained via OAuth (use--auto-fetch-credentialsto fetch automatically)
For the Edge Add-ons Store, you'll use the Microsoft Edge Publish API
2. Decide how to access the data & credentials
.env files method
Use the .env snippet(s) relevant to your extension
Include each one in your root directory
Make sure to have *.env or chrome.env, firefox.env, edge.env, opera.env in your .gitignore
Next, in the CLI:
web-ext-deploy envOptions for env mode
Global:
| Flag | Description |
|---|---|
--publish-only <stores...> |
Only deploy to specific stores (e.g. --publish-only chrome firefox) |
--auto-fetch-credentials |
Automatically fetch missing or expired credentials. Opera: fetches sessionid/csrftoken via Playwright (auto-installed if needed). Chrome: fetches REFRESH_TOKEN via OAuth. In env mode, saves fetched credentials to the store's .env file |
--dry-run |
Validate inputs without deploying |
--verbose |
Log each deployment step |
Chrome overrides:
| Flag | Description |
|---|---|
--chrome-skip-review |
Publish without waiting for a review |
--chrome-deploy-percentage <number> |
Staged rollout percentage (1-100) |
Firefox overrides:
| Flag | Description |
|---|---|
--firefox-changelog <text> |
Changelog for Firefox users. Supports \n for new lines |
--firefox-changelog-lang <code> |
Language of the changelog. Full list (default: en-US) |
--firefox-dev-changelog <text> |
Changelog for Firefox Add-ons reviewers only. Supports \n for new lines |
Edge overrides:
| Flag | Description |
|---|---|
--edge-dev-changelog <text> |
Changelog for Edge Add-ons reviewers only. Supports \n for new lines |
Opera overrides:
| Flag | Description |
|---|---|
--opera-changelog <text> |
Changelog for Opera users. Supports \n for new lines |
Notes:
-
Chrome Web Store:
REFRESH_TOKEN- follow this guide, or omit and runweb-ext-deploy env --auto-fetch-credentialsto fetch and save it automaticallyPUBLISHER_ID- Get it from the Chrome Web Store Developer Dashboard > AccountEXT_ID- Get it fromhttps://chromewebstore.google.com/detail/EXT_ID
-
Firefox Add-ons store:
EXT_ID- Get it fromhttps://addons.mozilla.org/addon/EXT_IDZIP- The relative path to the ZIP. You can use{version}, which will be replaced by theversionentry from yourpackage.jsonZIP_SOURCE- Optional. The relative path to the ZIP that contains the source code of your extension, if applicableJWT_ISSUER,JWT_SECRET- obtain from the Developer Hub
-
Edge Add-ons store:
CLIENT_ID,API_KEY- follow this guidePRODUCT_ID- Get it fromhttps://partner.microsoft.com/en-us/dashboard/microsoftedge/PRODUCT_IDZIP- You can use{version}
-
Opera Add-ons store:
PACKAGE_ID- Get it fromhttps://addons.opera.com/developer/package/PACKAGE_IDZIP- You can use{version}SESSIONID,CSRFTOKEN- can be omitted if you runweb-ext-deploy env --auto-fetch-credentials(fetches and saves them automatically via Playwright)- Source code inspection:
The Opera Add-ons reviewers require inspecting your extension's source code
This can be done by doing one of the following:- Uploading the ZIP that contains the source code to a public folder on a storage service (e.g. Google Drive)
- Making the extension's code open source on a platform like GitHub, with clear instructions on the
README.md, and then linking to its repository
-
The keys are case-insensitive, as they will be camel-cased anyway
Possible .env files
chrome.env
EXT_ID="ExtensionID"
PUBLISHER_ID="PublisherID"
REFRESH_TOKEN="RefreshToken"
ZIP="dist/some-zip-v{version}.zip"firefox.env
JWT_ISSUER="JwtIssuer"
JWT_SECRET="JwtSecret"
ZIP="dist/some-zip-v{version}.zip"
ZIP_SOURCE="dist/some-zip-source-v{version}.zip"
EXT_ID="ExtensionID"
CHANGELOG_LANG="en-US"edge.env
CLIENT_ID="ClientID"
API_KEY="ApiKey"
ZIP="dist/some-zip-v{version}.zip"
PRODUCT_ID="ProductID"opera.env
SESSIONID="sessionid_value"
CSRFTOKEN="csrftoken_value"
ZIP="dist/some-zip-v{version}.zip"
PACKAGE_ID=123456CLI arguments method
Use it only if your extension's code will not be publicly available
web-ext-deploy cli --chrome-zip="some-zip-v{version}.zip" --chrome-ext-id="ExtensionID" --firefox-zip="some-zip-v{version}.zip" --firefox-ext-id="ExtensionID"CLI API
Stores:
Options:
| Flag | Description |
|---|---|
--auto-fetch-credentials |
Automatically fetch missing or expired credentials. Opera: fetches sessionid/csrftoken via Playwright (auto-installed if needed). Chrome: fetches REFRESH_TOKEN via OAuth. In env mode, saves fetched credentials to the store's .env file |
--dry-run |
Validate inputs without deploying |
--verbose |
Log each deployment step |
Chrome Web Store CLI
| Flag | Description |
|---|---|
--chrome-ext-id <id> |
Extension ID from https://chromewebstore.google.com/detail/EXT_ID |
--chrome-publisher-id <id> |
Publisher ID from the Developer Dashboard > Account |
--chrome-client-id <id> |
OAuth client ID (required for --auto-fetch-credentials) |
--chrome-client-secret <secret> |
OAuth client secret (required for --auto-fetch-credentials) |
--chrome-refresh-token <token> |
OAuth refresh token. Can be omitted if --auto-fetch-credentials is set (how to get one) |
--chrome-zip <path> |
Path to the ZIP. Supports {version} placeholder |
[--chrome-skip-review] |
Publish without waiting for a review |
[--chrome-deploy-percentage <n>] |
Staged rollout percentage (1-100) |
Example:
web-ext-deploy cli --chrome-ext-id="ExtensionID" --chrome-publisher-id="PublisherID" --chrome-refresh-token="RefreshToken" --chrome-zip="some-zip-v{version}.zip"Or, fetch the refresh token automatically (opens a browser for OAuth consent):
web-ext-deploy cli --auto-fetch-credentials --chrome-ext-id="ExtensionID" --chrome-publisher-id="PublisherID" --chrome-client-id="ClientID" --chrome-client-secret="ClientSecret" --chrome-zip="some-zip-v{version}.zip"Firefox Add-ons CLI
| Flag | Description |
|---|---|
--firefox-ext-id <id> |
Extension ID from https://addons.mozilla.org/addon/EXT_ID |
--firefox-jwt-issuer <issuer> |
JWT issuer from the Developer Hub |
--firefox-jwt-secret <secret> |
JWT secret from the Developer Hub |
--firefox-zip <path> |
Path to the ZIP. Supports {version} placeholder |
[--firefox-zip-source <path>] |
Path to the source code ZIP. Supports {version} placeholder |
[--firefox-changelog <text>] |
Changelog for Firefox users. Supports \n for new lines |
[--firefox-changelog-lang <code>] |
Language of the changelog. Full list (default: en-US) |
[--firefox-dev-changelog <text>] |
Changelog for reviewers only. Supports \n for new lines |
Example:
web-ext-deploy cli --firefox-ext-id="ExtensionID" --firefox-jwt-issuer="JwtIssuer" --firefox-jwt-secret="JwtSecret" --firefox-zip="dist/some-zip-v{version}.zip"Edge Add-ons CLI
| Flag | Description |
|---|---|
--edge-product-id <id> |
Product ID from https://partner.microsoft.com/en-us/dashboard/microsoftedge/PRODUCT_ID |
--edge-client-id <id> |
Client ID (how to get one) |
--edge-api-key <key> |
API key (how to get one) |
--edge-zip <path> |
Path to the ZIP. Supports {version} placeholder |
[--edge-dev-changelog <text>] |
Changelog for reviewers only. Supports \n for new lines |
Example:
web-ext-deploy cli --edge-product-id="ProductID" --edge-client-id="clientId" --edge-api-key="apiKey" --edge-zip="dist/some-zip-v{version}.zip"Note:
Due to the way the Edge dashboard works, when an extension is being reviewed or its review has just been canceled, it will take about a minute until a cancellation will cause its state to change from "In review" to "In draft", after which the new version can be submitted
Therefore, if you publish after you had just published/canceled, expect to wait a little longer
Opera Add-ons CLI
| Flag | Description |
|---|---|
--opera-package-id <id> |
Package ID from https://addons.opera.com/developer/package/PACKAGE_ID |
--opera-sessionid <value> |
Session cookie. Use --auto-fetch-credentials to obtain automatically |
--opera-csrftoken <value> |
CSRF cookie. Use --auto-fetch-credentials to obtain automatically |
--opera-zip <path> |
Path to the ZIP. Supports {version} placeholder |
[--opera-changelog <text>] |
Changelog for Opera users. Supports \n for new lines |
Example:
web-ext-deploy cli --opera-package-id=123456 --opera-sessionid="sessionid_value" --opera-csrftoken="csrftoken_value" --opera-zip="dist/some-zip-v{version}.zip"Or, fetch the cookies automatically via Playwright:
web-ext-deploy cli --auto-fetch-credentials --opera-package-id=123456 --opera-zip="dist/some-zip-v{version}.zip"Notes:
-
Source code inspection:
The Opera Add-ons reviewers require inspecting your extension's source code.
This can be done by doing one of the following:- Uploading the ZIP that contains the source code to a public folder on a storage service like Google Drive
- Making the extension's code open source on a platform like GitHub, with clear instructions on the
README.md, and then linking to its repository.
Note that you do not want to store the command with your extension package, as the review team will have access to your credentials.