The ultimate automation tool for deploying to multiple extension stores simultaneously!
Made by avi12
Supported stores:
npm i -D web-ext-deploy
# or
pnpm i -D web-ext-deploy
# or
yarn add -D web-ext-deploy
or install globally
npm i -g web-ext-deploy
# or
pnpm i -g web-ext-deploy
# or
yarn global add web-ext-deploy
Deployment to Chrome Web Store: follow this guide. Deployment to Edge Add-ons Store: follow this guide.
sessionid
, csrftoken
If you have a hard time obtaining the cookie(s), you can run:
web-ext-deploy --get-cookies=opera
Note that for the Chrome Web Store, you'll use the Chrome Web Store Publish API. As for the Edge Add-ons Store, you'll use the Microsoft Edge Publish API.
.env
files methodUse the .env
snippet(s) relevant to your extension.
Include each one in your root directory.
Make sure to have *.env
in your .gitignore
Note that if you used the aforementioned --get-cookies
, it automatically added the .env
listing(s) to it.
To use the .env
files, in the CLI:
web-ext-deploy --env
.env
mode:--verbose
boolean?
If specified, the steps of every store will be logged to the console.
--publish-only
("chrome" | "firefox" | "edge" | "opera"
)[]?
If specified, for each specified store that has an .env
file, it will be deployed.
E.g. if you have chrome.env
, firefox.env
, opera.env
, and you run:
web-ext-deploy --env --publish-only=chrome firefox
It will only deploy to Chrome Web Store and Firefox Add-ons Store.
--zip
string?
If specified, it will be used for every .env
that the ZIP
is not specified.
--firefox-changelog
string?
If specified and firefox.env
exists, it will be used to provide changelog for the Firefox users.
New lines (\n
) are supported.
--firefox-dev-changelog
string?
If specified and firefox.env
exists, it will be used to provide changelog for the Firefox Add-ons reviewers.
New lines (\n
) are supported.
--edge-dev-changelog
string?
If specified and edge.env
exists, it will be used to provide changelog for the Edge Add-ons reviewers.
New lines (\n
) are supported.
--opera-changelog
string?
If specified and opera.env
exists, it will be used to provide changelog for the Opera users.
New lines (\n
) are supported.
Chrome Web Store:
REFRESH_TOKEN
, CLIENT_ID
, CLIENT_SECRET
- follow this guide.EXT_ID
- Get it from https://chrome.google.com/webstore/detail/EXT_ID
, e.g. https://chrome.google.com/webstore/detail/fcphghnknhkimeagdglkljinmpbagone
Firefox Add-ons store:
EXT_ID
- Get it from https://addons.mozilla.org/addon/EXT_ID
ZIP
- The relative path to the ZIP. You can use {version}
, which will be replaced by the version
entry from your package.json
ZIP_SOURCE
- Optional. The relative path to the ZIP that contains the source code of your extension, if applicable.JWT_ISSUER
, JWT_SECRET
- obtain from the Developer Hub.Edge Add-ons store:
CLIENT_ID
, CLIENT_SECRET
, ACCESS_TOKEN_URL
, ACCESS_TOKEN
- follow this guidePRODUCT_ID
- Get it from https://partner.microsoft.com/en-us/dashboard/microsoftedge/PRODUCT_ID
ZIP
- You can use {version}
Opera Add-ons store:
PACKAGE_ID
- Get it from https://addons.opera.com/developer/package/PACKAGE_ID
ZIP
- You can use {version}
README.md
, and then linking to its repository.The keys are case-insensitive, as they will be camel-cased anyway.
.env
fileschrome.env
REFRESH_TOKEN="RefreshToken"
CLIENT_ID="ClientID"
CLIENT_SECRET="ClientSecret"
ZIP="dist/some-zip-v{version}.zip"
EXT_ID="ExtensionID"
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"
edge.env
CLIENT_ID="ClientID"
CLIENT_SECRET="ClientSecret"
ACCESS_TOKEN_URL="AccessTokenURL"
ACCESS_TOKEN="AccessToken"
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=123456
Use it only if your extension's code will not be published.
web-ext-deploy --chrome-zip="some-zip-v{version}.zip" --chrome-ext-id="ExtensionID" --firefox-zip="some-zip-v{version}.zip" --firefox-ext-id="ExtensionID"
Stores:
Options:
--verbose
boolean?
If specified, the steps of every store will be logged to the console.
--zip
string?
If specified, it will be used for every store that the zip
is not specified.
For example, in
web-ext-deploy --zip="zip-v{version}.zip" --chrome-refresh-token="refreshToken" --firefox-sessionid="sessionid_value" --edge-zip="some-zip-v{version}.zip"
the zip-v{version}.zip
will be used for the Chrome Web Store version and the Firefox Add-ons version.
--chrome-ext-id
string
Get it from https://chrome.google.com/webstore/detail/EXT_ID
, e.g. https://chrome.google.com/webstore/detail/fcphghnknhkimeagdglkljinmpbagone
--chrome-refresh-token
string
The refreshToken you have registered.--chrome-client-id
string
The client ID you have registered.--chrome-client-secret
string
The client secret you have registered.--chrome-zip
string
The relative path to the ZIP from the root.
You can use {version}
in the ZIP filename, which will be replaced by the version in package.json
To get your --chrome-refresh-token
, --chrome-client-id
and --chrome-client-secret
, follow this guide.
Example:
web-ext-deploy --chrome-ext-id="ExtensionID" --chrome-refresh-token="RefreshToken" --chrome-client-id="ClientID" --chrome-client-secret="ClientSecret" --chrome-zip="some-zip-v{version}.zip"
--firefox-ext-id
string
The extension ID from the store URL, e.g. https://addons.mozilla.org/addon/EXT_ID
--firefox-jwt-issuer
string
The JWT issuer.--firefox-jwt-secret
string
The JWT secret.--firefox-zip
string
The relative path to the ZIP from the root.
You can use {version}
in the ZIP filename, which will be replaced by the version
entry from your package.json
--firefox-zip-source
string?
The relative path to the ZIP that contains the source code of your extension, if applicable.
You can use {version}
as well.
Note that if your extension's source code is required to be seen by the review team, you do not want to store the command with the package.--firefox-changelog
string?
The changes made in this version compared to the previous one. The Firefox users will see this.
You can use \n
for new lines.--firefox-dev-changelog
string?
The technical changes made in this version, which will be seen by the Firefox Add-ons reviewers.
You can use \n
for new lines.Get your --firefox-jwt-issuer
and --firefox-jwt-secret
from the Developer Hub.
Example:
web-ext-deploy --firefox-ext-id="ExtensionID" --firefox-jwt-issuer="JwtIssuer" --firefox-jwt-secret="JwtSecret" --firefox-zip="dist/some-zip-v{version}.zip" --firefox-changelog="Changelog\nWith line breaks" --firefox-dev-changelog="Changelog for reviewers\nWith line breaks"
--edge-product-id
string
The product ID from the Edge Add-ons Dashboard, e.g. https://partner.microsoft.com/en-us/dashboard/microsoftedge/PRODUCT_ID
--edge-client-id
string
The client ID.--edge-client-secret
string
The client secret.--edge-access-token-url
string
The access token URL.--edge-access-token
string
The access token.--edge-zip
string
The path to the ZIP from the root.
You can use {version}
in the ZIP filename, which will be replaced by the version
entry in package.json
--edge-dev-changelog
string?
The technical changes made in this version, which will be seen by the Edge Add-ons reviewers.
You can use \n
for new lines.To get your --edge-access-token
, --edge-client-id
, --edge-client-secret
, --edge-access-token-url
, follow this guide.
Example:
web-ext-deploy --edge-product-id="ProductID" --edge-access-token="accessToken value" --edge-client-id="clientId" --edge-client-secret="clientSecret" --edge-access-token-url="accessTokenUrl" --edge-zip="dist/some-zip-v{version}.zip" --edge-dev-changelog="Changelog for reviewers\nWith line breaks"
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, expect for longer wait times if you run the tool on an extension you had just published/canceled.
--opera-package-id
number
The extension ID from the Opera Add-ons Dashboard, e.g. https://addons.opera.com/developer/package/PACKAGE_ID
--opera-sessionid
string
The value of the cookie sessionid
, which will be used to log in to the publisher's account.--opera-csrftoken
string
The value of the cookie csrftoken
, which will be used to upload the ZIP.--opera-zip
string
The relative path to the ZIP from the root.
You can use {version}
in the ZIP filename, which will be replaced by the version
entry in package.json
--opera-changelog
string?
The changes made in this version, which will be seen by the Opera Add-ons reviewers.
You can use \n
for new lines.Example:
web-ext-deploy --opera-package-id=123456 --opera-sessionid="sessionid_value" --opera-csrftoken="csrftoken_value" --opera-zip="dist/some-zip-v{version}.zip" --opera-changelog="Changelog\nWith line breaks"
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:
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 precious cookies.
import { deployChrome, deployFirefoxSubmissionApi, deployEdgePublishApi, deployOpera } from "web-ext-deploy";
deployChrome
object
Options:
extId
string
Get it from https://chrome.google.com/webstore/detail/EXT_ID
, e.g. https://chrome.google.com/webstore/detail/fcphghnknhkimeagdglkljinmpbagone
refreshToken
string
The refresh token.clientId
string
The client ID.clientSecret
string
The client secret.zip
string
The relative path from the root to the ZIP.
You can use {version}
to use the version
entry from your package.json
verbose
boolean?
If true
, it will be logged to the console when the uploading has begun.To get your refreshToken
, clientId
, and clientSecret
, follow this guide.
Returns Promise<true>
or throws an exception.
deployFirefoxSubmissionApi
object
Options:
extId
string
Get it from https://addons.mozilla.org/addon/EXT_ID
jwtIssuer
string
The JWT issuer.jwtSecret
string
The JWT secret.zip
string
The relative path from the root to the ZIP.
You can use {version}
in the ZIP filename, which will be replaced by the version
entry from your package.json
zipSource
string?
The relative path from the root to the ZIP that contains the source code of your extension, if applicable.
You can use {version}
as well.
Note that if your extension's source code is required to be seen by the review team, you do not want to store the deployment script with the package.changelog
string?
The changes made in this version, compared to the previous one, which will be seen by the Firefox users.
I recommend providing the changelog via --firefox-changelog
, so it stays dynamic.devChangelog
string?
The technical changes made in this version, compared to the previous one, which will be visible only to the Firefox Add-ons reviewers.
I recommend providing the changelog via --firefox-dev-changelog
, so it stays up to date.verbose
boolean?
If true
, every step of uploading to the Firefox Add-ons will be logged to the console.Get your jwtIssuer
and jwtSecret
from the Developer Hub.
Returns Promise<true>
or throws an exception.
deployEdgePublishApi
object
Options:
productId
string
Get it from https://partner.microsoft.com/en-us/dashboard/microsoftedge/PRODUCT_ID
accessToken
string
The access token.
clientId
string
The client ID.
clientSecret
string
The client secret.
accessTokenUrl
string
The access token URL.
zip
string
The relative path from the root to the ZIP.
You can use {version}
in the ZIP filename, which will be replaced by the version
entry from your package.json
devChangelog
string?
The technical changes made in this version, compared to the previous one, which will be visible only to the Edge Add-ons reviewers.
I recommend providing the changelog via --edge-dev-changelog
, so it stays up to date.
verbose
boolean?
If true
, every step of uploading to the Edge Add-ons will be logged to the console.
To get your accessToken
, clientId
, clientSecret
, and accessTokenUrl
, follow this guide.
Returns Promise<true>
or throws an exception.
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, expect for longer wait times if you run the tool on an extension you had just published/canceled.
deployOpera
object
Options:
packageId
number
The package ID of the extension from the store dashboard, e.g. https://addons.opera.com/developer/package/PACKAGE_ID
sessionid
string
The value of the cookie sessionid
, which will be used to log in to the publisher's account.csrftoken
string
The value of the cookie csrftoken
, which will be used to upload the ZIP.zip
string
The relative path from the root to the ZIP.
You can use {version}
in the ZIP filename, which will be replaced by the version
entry from your package.json
changelog
string?
The changes made in this version, compared to the previous one, which will be seen by the Opera users.
I recommend providing the changelog via --opera-changelog
, so it stays up to date.verbose
boolean?
If true
, every step of uploading to the Opera Add-ons will be logged to the console.If you have a hard time obtaining the values of the cookies sessionid
and csrftoken
, you can run:
web-ext-deploy --get-cookies=opera
Returns Promise<true>
or throws an exception.
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:
README.md
, and then linking to its repository.Note that you do not want to store the deployment script with your extension package, as the review team will have access to your precious cookies.
If you'll open-source the extension on GitHub, you can exclude the deployment script by listing it in .gitignore
Examples:
import { deployChrome, deployFirefoxSubmissionApi, deployEdgePublishApi, deployOpera } from "web-ext-deploy";
deployChrome({
extId: "ExtensionID",
refreshToken: "refreshToken",
clientId: "clientId",
clientSecret: "clientSecret",
zip: "dist/some-zip-v{version}.zip",
verbose: false
}).catch(console.error);
deployFirefoxSubmissionApi({
extId: "EXT_ID",
jwtIssuer: "jwtIssuer",
jwtSecret: "jwtSecret",
zip: "dist/some-zip-v{version}.zip",
zipSource: "dist/zip-source-v{version}.zip",
changelog: "Some changes",
devChangelog: "Changes for reviewers",
verbose: false
}).catch(console.error);
deployEdgePublishApi({
productId: "PRODUCT_ID",
clientId: "clientId",
clientSecret: "clientSecret",
accessTokenUrl: "accessTokenUrl",
accessToken: "accessToken",
zip: "dist/some-zip-v{version}.zip",
devChangelog: "Changes for reviewers",
verbose: false
}).catch(console.error);
deployOpera({
packageId: 123456,
sessionid: "sessionid_value",
csrftoken: "csrftoken_value",
zip: "dist/some-zip-v{version}.zip",
changelog: "Some changes",
verbose: false
}).catch(console.error);