Node.js 18+ resolves DNS with IPv6 preference by default. On Linux systems where IPv6 routing to addons.mozilla.org is broken or unavailable, this causes web-ext's fetch to silently fail. Setting --dns-result-order=ipv4first via NODE_OPTIONS ensures IPv4 is tried first. https://claude.ai/code/session_01G6wo3QBwKjEgAM7j5hQkx4
152 lines
4.4 KiB
Bash
Executable File
152 lines
4.4 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Sign the Firefox extension using Mozilla's API.
|
|
# Tries the current version first. Only bumps if that version
|
|
# already exists at Mozilla. Handles rate limiting with backoff.
|
|
#
|
|
# Reads credentials from .env file.
|
|
#
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
ENV_FILE="$SCRIPT_DIR/.env"
|
|
MANIFEST="$SCRIPT_DIR/manifest.firefox.json"
|
|
MANIFEST_CHROME="$SCRIPT_DIR/manifest.json"
|
|
PACKAGE_JSON="$SCRIPT_DIR/package.json"
|
|
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
echo "Error: .env file not found."
|
|
echo " cp .env.example .env"
|
|
echo " Then edit .env with your Mozilla API credentials."
|
|
exit 1
|
|
fi
|
|
|
|
# --- Read current version (don't bump yet — try current first) ---
|
|
CURRENT_VERSION=$(grep -o '"version": "[^"]*"' "$MANIFEST" | head -1 | grep -o '[0-9.]*')
|
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
|
|
NEW_VERSION="$CURRENT_VERSION"
|
|
|
|
echo "Current version: $CURRENT_VERSION"
|
|
|
|
# --- Parse .env ---
|
|
API_KEY=""
|
|
API_SECRET=""
|
|
|
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
[[ -z "$line" ]] && continue
|
|
|
|
key="${line%%=*}"
|
|
value="${line#*=}"
|
|
|
|
key="$(echo "$key" | tr -d '[:space:]')"
|
|
|
|
value="${value#\"}"
|
|
value="${value%\"}"
|
|
value="${value#\'}"
|
|
value="${value%\'}"
|
|
value="$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
|
|
|
case "$key" in
|
|
WEB_EXT_API_KEY) API_KEY="$value" ;;
|
|
WEB_EXT_API_SECRET) API_SECRET="$value" ;;
|
|
esac
|
|
done < "$ENV_FILE"
|
|
|
|
if [ -z "$API_KEY" ]; then
|
|
echo "Error: WEB_EXT_API_KEY not found in .env"
|
|
echo ""
|
|
echo "Your .env should look like:"
|
|
echo ' WEB_EXT_API_KEY=user:12345:678'
|
|
echo ' WEB_EXT_API_SECRET=your-secret-here'
|
|
echo ""
|
|
echo "Get credentials at: https://addons.mozilla.org/developers/addon/api/key/"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$API_SECRET" ]; then
|
|
echo "Error: WEB_EXT_API_SECRET not found in .env"
|
|
exit 1
|
|
fi
|
|
|
|
echo "API Key: ${API_KEY:0:10}..."
|
|
echo ""
|
|
|
|
# Build
|
|
echo "Building Firefox extension..."
|
|
"$SCRIPT_DIR/build.sh" firefox
|
|
|
|
# --- Sign with retry ---
|
|
MAX_ATTEMPTS=5
|
|
WAIT_TIME=10
|
|
|
|
for attempt in $(seq 1 $MAX_ATTEMPTS); do
|
|
echo ""
|
|
echo "=== Attempt $attempt: signing v$NEW_VERSION ==="
|
|
|
|
# Capture output to check for specific errors
|
|
# NODE_OPTIONS forces IPv4 DNS resolution first — Node.js 18+ defaults to IPv6,
|
|
# which silently fails when the system can't reach addons.mozilla.org over IPv6.
|
|
OUTPUT=$(NODE_OPTIONS='--dns-result-order=ipv4first' npx web-ext sign \
|
|
--no-config-discovery \
|
|
--source-dir "$SCRIPT_DIR/dist/firefox" \
|
|
--artifacts-dir "$SCRIPT_DIR/dist/firefox-signed" \
|
|
--channel unlisted \
|
|
--api-key "$API_KEY" \
|
|
--api-secret "$API_SECRET" 2>&1) && {
|
|
echo "$OUTPUT"
|
|
echo ""
|
|
echo "Success! v$NEW_VERSION signed."
|
|
echo "Install: dist/firefox-signed/"
|
|
|
|
# Update source files to match the signed version
|
|
sed -i "s/^ \"version\": \"[^\"]*\"/ \"version\": \"$NEW_VERSION\"/" "$MANIFEST"
|
|
sed -i "s/^ \"version\": \"[^\"]*\"/ \"version\": \"$NEW_VERSION\"/" "$MANIFEST_CHROME"
|
|
sed -i "s/^ \"version\": \"[^\"]*\"/ \"version\": \"$NEW_VERSION\"/" "$PACKAGE_JSON"
|
|
|
|
cd "$SCRIPT_DIR"
|
|
git add manifest.json manifest.firefox.json package.json 2>/dev/null
|
|
git commit -m "chore: release v$NEW_VERSION (Firefox signed)" --allow-empty 2>/dev/null || true
|
|
|
|
exit 0
|
|
}
|
|
|
|
echo "$OUTPUT"
|
|
|
|
# Check if rate limited
|
|
if echo "$OUTPUT" | grep -q "throttled"; then
|
|
# Extract wait time from error message
|
|
THROTTLE_SECS=$(echo "$OUTPUT" | grep -oP 'available in \K\d+' || echo "60")
|
|
echo ""
|
|
echo "Rate limited by Mozilla. Waiting ${THROTTLE_SECS}s..."
|
|
sleep "$THROTTLE_SECS"
|
|
# Don't bump version — retry the same version after cooldown
|
|
continue
|
|
fi
|
|
|
|
# Check if version already exists
|
|
if echo "$OUTPUT" | grep -qi "already exists\|version.*conflict\|could not be uploaded"; then
|
|
PATCH=$((PATCH + 1))
|
|
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
echo ""
|
|
echo "Version conflict. Bumping to $NEW_VERSION..."
|
|
|
|
# Update only the built manifest (not source — we'll update source on success)
|
|
sed -i "s/^ \"version\": \"[^\"]*\"/ \"version\": \"$NEW_VERSION\"/" "$SCRIPT_DIR/dist/firefox/manifest.json"
|
|
|
|
sleep "$WAIT_TIME"
|
|
continue
|
|
fi
|
|
|
|
# Unknown error — wait and retry
|
|
echo ""
|
|
echo "Unknown error. Waiting ${WAIT_TIME}s before retry..."
|
|
sleep "$WAIT_TIME"
|
|
done
|
|
|
|
echo ""
|
|
echo "Error: Failed after $MAX_ATTEMPTS attempts."
|
|
echo "If rate limited, wait a few minutes and try again."
|
|
exit 1
|