- build Firefox XPI and Chrome ZIP on every push - upload packages as downloadable workflow artifacts - sign and submit Firefox releases to AMO - upload and publish Chrome releases to Chrome Web Store - attach signed packages to GitHub release cards - document required publishing secrets and release workflow
197 lines
7.2 KiB
YAML
197 lines
7.2 KiB
YAML
name: Build and publish extension
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- "**"
|
|
release:
|
|
types: [published]
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Version to validate (for example 1.0.1)"
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
name: Build extension packages
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Check out source
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.release.tag_name || github.ref }}
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.x"
|
|
|
|
- name: Validate release version
|
|
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
|
|
env:
|
|
RELEASE_VERSION: ${{ github.event.release.tag_name || inputs.version }}
|
|
run: |
|
|
python - <<'PY'
|
|
import json, os, re
|
|
expected = os.environ["RELEASE_VERSION"].removeprefix("v")
|
|
for path in ("manifest.json", "manifest.chrome.json"):
|
|
actual = json.load(open(path, encoding="utf-8"))["version"]
|
|
if actual != expected:
|
|
raise SystemExit(f"{path}: version {actual!r} does not match release {expected!r}")
|
|
if not re.fullmatch(r"\d+(\.\d+){1,3}", expected):
|
|
raise SystemExit(f"Release version must be numeric, got {expected!r}")
|
|
PY
|
|
|
|
- name: Install web-ext
|
|
run: npm install --global web-ext
|
|
|
|
- name: Prepare Firefox source
|
|
run: |
|
|
mkdir -p dist/firefox-source
|
|
rsync -a \
|
|
--exclude '.git/' \
|
|
--exclude '.github/' \
|
|
--exclude 'web-ext-artifacts/' \
|
|
--exclude 'manifest.chrome.json' \
|
|
--exclude 'background-chrome.js' \
|
|
--exclude 'background-service-worker.js' \
|
|
--exclude 'browser-api.js' \
|
|
--exclude 'scripts/' \
|
|
./ dist/firefox-source/
|
|
|
|
- name: Build Firefox package
|
|
run: |
|
|
web-ext build \
|
|
--source-dir dist/firefox-source \
|
|
--artifacts-dir dist/firefox \
|
|
--filename proxyswitcherforkbywt-firefox.xpi \
|
|
--overwrite-dest
|
|
|
|
- name: Build Chrome package
|
|
run: python scripts/build-chrome.py
|
|
|
|
- name: Upload packages for this push
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: extension-packages-${{ github.sha }}
|
|
if-no-files-found: error
|
|
path: |
|
|
dist/firefox/proxyswitcherforkbywt-firefox.xpi
|
|
web-ext-artifacts/proxyswitcherforkbywt-chrome.zip
|
|
|
|
publish:
|
|
name: Publish stores and attach release assets
|
|
if: github.event_name == 'release' && github.event.action == 'published'
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
env:
|
|
AMO_JWT_ISSUER: ${{ secrets.AMO_JWT_ISSUER }}
|
|
AMO_JWT_SECRET: ${{ secrets.AMO_JWT_SECRET }}
|
|
CWS_PUBLISHER_ID: ${{ secrets.CWS_PUBLISHER_ID }}
|
|
CWS_EXTENSION_ID: ${{ secrets.CWS_EXTENSION_ID }}
|
|
CWS_CLIENT_ID: ${{ secrets.CWS_CLIENT_ID }}
|
|
CWS_CLIENT_SECRET: ${{ secrets.CWS_CLIENT_SECRET }}
|
|
CWS_REFRESH_TOKEN: ${{ secrets.CWS_REFRESH_TOKEN }}
|
|
steps:
|
|
- name: Check out release source
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.release.tag_name }}
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
|
|
- name: Download build packages
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: extension-packages-${{ github.sha }}
|
|
path: dist/packages
|
|
|
|
- name: Set up Firefox signing tools
|
|
run: |
|
|
npm install --global web-ext
|
|
mkdir -p dist/firefox-source
|
|
rsync -a \
|
|
--exclude '.git/' \
|
|
--exclude '.github/' \
|
|
--exclude 'web-ext-artifacts/' \
|
|
--exclude 'manifest.chrome.json' \
|
|
--exclude 'background-chrome.js' \
|
|
--exclude 'background-service-worker.js' \
|
|
--exclude 'browser-api.js' \
|
|
--exclude 'scripts/' \
|
|
./ dist/firefox-source/
|
|
|
|
- name: Sign and submit Firefox package to AMO
|
|
run: |
|
|
test -n "$AMO_JWT_ISSUER" || { echo 'Missing AMO_JWT_ISSUER secret'; exit 1; }
|
|
test -n "$AMO_JWT_SECRET" || { echo 'Missing AMO_JWT_SECRET secret'; exit 1; }
|
|
web-ext sign \
|
|
--source-dir dist/firefox-source \
|
|
--artifacts-dir dist/firefox-signed \
|
|
--api-key "$AMO_JWT_ISSUER" \
|
|
--api-secret "$AMO_JWT_SECRET" \
|
|
--channel listed \
|
|
--approval-timeout 900000 \
|
|
--no-input
|
|
firefox_xpi=$(find dist/firefox-signed -maxdepth 1 -name '*.xpi' -print -quit)
|
|
test -n "$firefox_xpi" || { echo 'AMO did not return a signed XPI'; exit 1; }
|
|
cp "$firefox_xpi" dist/packages/proxyswitcherforkbywt-firefox-signed.xpi
|
|
|
|
- name: Upload Chrome package to Chrome Web Store
|
|
run: |
|
|
set -euo pipefail
|
|
for name in CWS_PUBLISHER_ID CWS_EXTENSION_ID CWS_CLIENT_ID CWS_CLIENT_SECRET CWS_REFRESH_TOKEN; do
|
|
test -n "${!name}" || { echo "Missing $name secret"; exit 1; }
|
|
done
|
|
token=$(curl --fail --silent --show-error \
|
|
--data-urlencode "client_id=$CWS_CLIENT_ID" \
|
|
--data-urlencode "client_secret=$CWS_CLIENT_SECRET" \
|
|
--data-urlencode "refresh_token=$CWS_REFRESH_TOKEN" \
|
|
--data-urlencode 'grant_type=refresh_token' \
|
|
https://oauth2.googleapis.com/token | python -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
|
|
chrome_zip=$(find dist/packages -name 'proxyswitcherforkbywt-chrome.zip' -print -quit)
|
|
test -f "$chrome_zip" || { echo "Missing Chrome package: $chrome_zip"; exit 1; }
|
|
curl --fail --silent --show-error \
|
|
-X POST \
|
|
-H "Authorization: Bearer $token" \
|
|
-H 'Content-Type: application/zip' \
|
|
--data-binary "@$chrome_zip" \
|
|
"https://chromewebstore.googleapis.com/upload/v2/publishers/$CWS_PUBLISHER_ID/items/$CWS_EXTENSION_ID:upload"
|
|
curl --fail --silent --show-error \
|
|
-X POST \
|
|
-H "Authorization: Bearer $token" \
|
|
"https://chromewebstore.googleapis.com/v2/publishers/$CWS_PUBLISHER_ID/items/$CWS_EXTENSION_ID:publish"
|
|
|
|
- name: Attach signed Firefox and Chrome packages to release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
gh release upload "${{ github.event.release.tag_name }}" \
|
|
dist/packages/proxyswitcherforkbywt-firefox-signed.xpi \
|
|
"$chrome_zip" \
|
|
--clobber
|
|
|
|
- name: Upload signed packages as workflow artifacts
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: signed-extension-packages-${{ github.event.release.tag_name }}
|
|
if-no-files-found: error
|
|
path: |
|
|
dist/packages/proxyswitcherforkbywt-firefox-signed.xpi
|
|
dist/packages/**/proxyswitcherforkbywt-chrome.zip
|