@extension.dev/deploy
v0.2.2
Published
Deploy browser extensions to Chrome Web Store, Firefox AMO, and Edge Partner Center.
Maintainers
Readme
@extension.dev/deploy
Deploy browser extensions to Chrome Web Store, Firefox AMO, and Edge Partner Center from a single command.
- Upload and publish to one, two, or all three stores in a single run
- Works as a CLI or as a Node.js library
- Reads credentials from CLI flags, environment variables, or a
.env.submitfile - Dry-run mode to verify auth without uploading anything
- Chrome staged rollout and expedited review support
- Zero runtime dependencies beyond zod
Install
npm install @extension.dev/deployQuick start
If you're setting up a new project, run the interactive wizard. It walks you
through each store's credential acquisition (including auto-generating a
Chrome refresh token from your OAuth client), runs a verification check
against each store's API, and writes everything to a .env.submit file:
npx extension-deploy initNon-interactive use (e.g. CI setup scripts):
npx extension-deploy init \
--init-stores chrome,firefox \
--init-output config/.env.submit \
--init-skip-verify \
--init-forceAfter init has written .env.submit, a full submission is a one-liner:
npx extension-deploy --dry-run # validate creds and ZIPs
npx extension-deploy # actually submitCLI usage
If you'd rather skip the wizard and pass credentials directly:
npx extension-deploy \
--chrome-zip dist/chrome.zip \
--chrome-extension-id abcdefghijklmnopabcdefghijklmnop \
--chrome-client-id 123.apps.googleusercontent.com \
--chrome-client-secret GOCSPX-xxx \
--chrome-refresh-token 1//0xxxRun extension-deploy --help for the full list of flags.
Global flags
| Flag | Description |
| ----------- | ------------------------------------------------------------- |
| --dry-run | Verify auth and validate ZIPs without uploading or publishing |
| --help | Show the help message |
Chrome Web Store
| Flag | Env var | Description |
| ---------------------------------- | --------------------------- | --------------------------------- |
| --chrome-zip <path> | CHROME_ZIP | Path to the extension ZIP file |
| --chrome-extension-id <id> | CHROME_EXTENSION_ID | Chrome Web Store extension ID |
| --chrome-client-id <id> | CHROME_CLIENT_ID | OAuth2 client ID |
| --chrome-client-secret <secret> | CHROME_CLIENT_SECRET | OAuth2 client secret |
| --chrome-refresh-token <token> | CHROME_REFRESH_TOKEN | OAuth2 refresh token |
| --chrome-publish-target <target> | CHROME_PUBLISH_TARGET | "default" or "trustedTesters" |
| --chrome-deploy-percentage <n> | CHROME_DEPLOY_PERCENTAGE | Staged rollout percentage (1-100) |
| --chrome-review-exemption | CHROME_REVIEW_EXEMPTION | Request expedited review |
| --chrome-skip-submit-review | CHROME_SKIP_SUBMIT_REVIEW | Upload only, skip publish step |
Firefox AMO
| Flag | Env var | Description |
| ------------------------------- | ---------------------- | ------------------------------------------ |
| --firefox-zip <path> | FIREFOX_ZIP | Path to the extension ZIP file |
| --firefox-sources-zip <path> | FIREFOX_SOURCES_ZIP | Path to sources ZIP (optional, for review) |
| --firefox-extension-id <id> | FIREFOX_EXTENSION_ID | Addon GUID ({uuid}) or email-style ID |
| --firefox-jwt-issuer <issuer> | FIREFOX_JWT_ISSUER | AMO JWT issuer (API key) |
| --firefox-jwt-secret <secret> | FIREFOX_JWT_SECRET | AMO JWT secret |
| --firefox-channel <channel> | FIREFOX_CHANNEL | "listed" or "unlisted" |
Edge Partner Center
| Flag | Env var | Description |
| --------------------------- | ------------------------- | ------------------------------ |
| --edge-zip <path> | EDGE_ZIP | Path to the extension ZIP file |
| --edge-product-id <id> | EDGE_PRODUCT_ID | Partner Center product ID |
| --edge-client-id <id> | EDGE_CLIENT_ID | Partner Center client ID |
| --edge-api-key <key> | EDGE_API_KEY | Partner Center API key (v1.1) |
| --edge-skip-submit-review | EDGE_SKIP_SUBMIT_REVIEW | Upload only, skip publish step |
Environment variables
All flags can be set via SCREAMING_SNAKE_CASE environment variables (e.g. CHROME_ZIP, FIREFOX_JWT_SECRET). CLI flags take precedence over environment variables.
A .env.submit file in the current working directory is loaded automatically. Existing environment variables are never overwritten.
# .env.submit
CHROME_ZIP=dist/chrome.zip
CHROME_EXTENSION_ID=abcdefghijklmnopabcdefghijklmnop
CHROME_CLIENT_ID=123.apps.googleusercontent.com
CHROME_CLIENT_SECRET=GOCSPX-xxx
CHROME_REFRESH_TOKEN=1//0xxxLibrary usage
import { deploy } from "@extension.dev/deploy";
const result = await deploy({
dryRun: false,
chrome: {
zip: "dist/chrome.zip",
extensionId: "abcdefghijklmnopabcdefghijklmnop",
clientId: "123.apps.googleusercontent.com",
clientSecret: "GOCSPX-xxx",
refreshToken: "1//0xxx",
publishTarget: "default",
deployPercentage: 10, // optional: staged rollout
},
firefox: {
zip: "dist/firefox.zip",
extensionId: "{12345678-1234-1234-1234-123456789abc}",
jwtIssuer: "user:12345:678",
jwtSecret: "your-jwt-secret",
channel: "listed",
},
edge: {
zip: "dist/edge.zip",
productId: "your-product-id",
clientId: "your-client-id",
apiKey: "your-api-key",
},
});
if (!result.success) {
for (const store of result.stores) {
if (!store.success) {
console.error(`${store.store} failed: ${store.error}`);
}
}
process.exit(1);
}deploy(config): Promise<DeployResult>
Deploys to all configured stores in parallel. Only stores with a config object are included — omit a store to skip it.
DeployConfig
| Field | Type | Required | Description |
| --------- | ---------------- | -------- | ---------------------------------------------- |
| dryRun | boolean | No | Verify auth without uploading. Default false |
| chrome | ChromeOptions | No | Chrome Web Store config |
| firefox | FirefoxOptions | No | Firefox AMO config |
| edge | EdgeOptions | No | Edge Partner Center config |
DeployResult
| Field | Type | Description |
| --------- | --------------- | ------------------------------ |
| dryRun | boolean | Whether this was a dry run |
| success | boolean | true if all stores succeeded |
| stores | StoreResult[] | Per-store results |
StoreResult
| Field | Type | Description |
| ---------- | ---------------------------------------------------- | ---------------------------- |
| store | "chrome" \| "firefox" \| "edge" | Store identifier |
| success | boolean | Whether submission succeeded |
| status | "dry_run" \| "submitted" \| "uploaded" \| "failed" | Result status |
| error | string? | Error message on failure |
| duration | number | Duration in milliseconds |
Validation schemas
Zod schemas are exported for custom validation:
import {
chromeOptionsSchema,
firefoxOptionsSchema,
edgeOptionsSchema,
deployConfigSchema,
} from "@extension.dev/deploy";CI example (GitHub Actions)
- name: Deploy extension
run: npx extension-deploy
env:
CHROME_ZIP: dist/chrome.zip
CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
FIREFOX_ZIP: dist/firefox.zip
FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }}
FIREFOX_JWT_ISSUER: ${{ secrets.FIREFOX_JWT_ISSUER }}
FIREFOX_JWT_SECRET: ${{ secrets.FIREFOX_JWT_SECRET }}
EDGE_ZIP: dist/edge.zip
EDGE_PRODUCT_ID: ${{ secrets.EDGE_PRODUCT_ID }}
EDGE_CLIENT_ID: ${{ secrets.EDGE_CLIENT_ID }}
EDGE_API_KEY: ${{ secrets.EDGE_API_KEY }}Exit codes
| Code | Meaning |
| ---- | ---------------------------------------------------------------- |
| 0 | All configured stores succeeded |
| 1 | One or more stores failed, or a validation/config error occurred |
Local development
pnpm install
pnpm lint
pnpm build
pnpm testRelease
This repository publishes through the Release GitHub Actions workflow.
Prerequisites:
- Add an
NPM_TOKENrepository secret with publish access for@extension.dev/deploy - Trigger the
Releaseworkflow from GitHub Actions with a plain semver like0.1.0
What the workflow does:
- Validates that the requested version and git tag do not already exist
- Installs dependencies, then runs lint, build, and test
- Updates
package.json - Creates commit
release: v<version>and tagv<version> - Pushes the commit and tag
- Publishes the package to npm
License
MIT
