DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

How I Used GitHub Actions to Auto-Publish to AMO on Every Release

How I Used GitHub Actions to Auto-Publish to AMO on Every Release

Manually uploading extension files to AMO (Mozilla's Add-On Observatory) is tedious. After the fifth time forgetting to increment the version number, I automated it with GitHub Actions.

Here's exactly how I set up the pipeline for the Weather & Clock Dashboard extension.

What the Pipeline Does

  1. Trigger on new GitHub release
  2. Validate the manifest version matches the release tag
  3. Bundle the extension into a .zip
  4. Submit to AMO via the web-ext CLI
  5. Wait for AMO review completion (or fail gracefully)

Setting Up AMO API Credentials

First, create API credentials at addons.mozilla.org:

  1. Go to AMO → User menu → Developer Hub → API Credentials
  2. Create a new credential
  3. Note your JWT issuer and JWT secret

Store these in GitHub Secrets:

AMO_JWT_ISSUER = user:12345678:123
AMO_JWT_SECRET = your-secret-here
Enter fullscreen mode Exit fullscreen mode

The Workflow

# .github/workflows/publish.yml
name: Publish to AMO

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install web-ext
        run: npm install -g web-ext

      - name: Verify version matches release tag
        run: |
          MANIFEST_VERSION=$(node -p "require('./manifest.json').version")
          RELEASE_TAG="${GITHUB_REF#refs/tags/v}"
          echo "Manifest version: $MANIFEST_VERSION"
          echo "Release tag: $RELEASE_TAG"
          if [ "$MANIFEST_VERSION" != "$RELEASE_TAG" ]; then
            echo "Version mismatch! manifest.json has $MANIFEST_VERSION but release is $RELEASE_TAG"
            exit 1
          fi
          echo "Version match confirmed: $MANIFEST_VERSION"

      - name: Build extension package
        run: |
          web-ext build \
            --source-dir . \
            --artifacts-dir ./web-ext-artifacts \
            --overwrite-dest
          ls -la ./web-ext-artifacts/

      - name: Sign and publish to AMO
        run: |
          web-ext sign \
            --source-dir . \
            --api-key "$AMO_JWT_ISSUER" \
            --api-secret "$AMO_JWT_SECRET" \
            --channel listed
        env:
          AMO_JWT_ISSUER: ${{ secrets.AMO_JWT_ISSUER }}
          AMO_JWT_SECRET: ${{ secrets.AMO_JWT_SECRET }}

      - name: Upload artifact to GitHub Release
        uses: softprops/action-gh-release@v1
        with:
          files: ./web-ext-artifacts/*.zip
Enter fullscreen mode Exit fullscreen mode

The web-ext sign Command Explained

web-ext sign \
  --source-dir .              # Where your extension files are
  --api-key $AMO_JWT_ISSUER   # From AMO API credentials  
  --api-secret $AMO_JWT_SECRET # From AMO API credentials
  --channel listed            # 'listed' for public, 'unlisted' for private
  --timeout 900000            # 15 min timeout for review queue
Enter fullscreen mode Exit fullscreen mode

Important: --channel listed means your extension goes through AMO's review process. The command will wait (up to the timeout) for AMO to review and approve the submission.

For unlisted, the signing happens immediately.

Handling AMO Review Delays

AMO review can take anywhere from hours to days. For listed extensions, the web-ext sign command might time out. Handle this gracefully:

- name: Submit to AMO
  continue-on-error: true  # Don't fail the workflow if review is pending
  run: |
    web-ext sign \
      --api-key "$AMO_JWT_ISSUER" \
      --api-secret "$AMO_JWT_SECRET" \
      --channel listed \
      --timeout 300000  # 5 minutes, then continue
Enter fullscreen mode Exit fullscreen mode

Local Testing with web-ext

Before triggering CI, test the web-ext commands locally:

# Install globally
npm install -g web-ext

# Build and check for errors
web-ext build

# Lint your extension
web-ext lint

# Run in Firefox for local testing
web-ext run --firefox=/Applications/Firefox.app/Contents/MacOS/firefox
Enter fullscreen mode Exit fullscreen mode

Creating a Release to Trigger the Workflow

# 1. Update version in manifest.json
jq '.version = "1.2.0"' manifest.json > tmp.json && mv tmp.json manifest.json

# 2. Commit and push
git add manifest.json && git commit -m "chore: bump version to 1.2.0"
git push

# 3. Tag and create release
git tag v1.2.0
git push origin v1.2.0

# Then create the release in GitHub UI or via CLI:
gh release create v1.2.0 --title "v1.2.0" --notes "What changed"
Enter fullscreen mode Exit fullscreen mode

This triggers the workflow and publishes to AMO automatically.

What Gets Submitted

The web-ext CLI automatically excludes files listed in .gitignore and certain development files. I also add a .web-ext-config.json:

{
  "build": {
    "overwriteDest": true,
    "filename": "weather-clock-dashboard-{version}.zip"
  },
  "ignoreFiles": [
    ".github",
    "*.md",
    "tests/**",
    "scripts/**",
    "node_modules"
  ]
}
Enter fullscreen mode Exit fullscreen mode

The Result

Now every GitHub release automatically:

  1. Validates version consistency
  2. Publishes to AMO
  3. Attaches the built .zip to the release

Check out the extension: Weather & Clock Dashboard on AMO


Part of a series on building and publishing Firefox browser extensions.

firefox #github #cicd #webdev #browserextension

Top comments (0)