#!/bin/bash # Script de construcción para Classic Add-ons Archive v3.0 # Genera paquetes .xpi (Firefox) y .zip (Chrome) set -e # Salir si hay errores echo "==================================================" echo " Classic Add-ons Archive - Build Script v3.0" echo "==================================================" echo "" # Colores para output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuración VERSION="3.0.0" DIST_DIR="dist" KEYS_DIR="private-keys" FIREFOX_PACKAGE="ca-archive-${VERSION}.xpi" CHROME_PACKAGE="ca-archive-${VERSION}-chrome.zip" CHROME_CRX="ca-archive-${VERSION}-chrome.crx" CHROME_KEY="$KEYS_DIR/chrome-extension.pem" FIREFOX_JWT="$KEYS_DIR/firefox-amo-credentials.json" # Opciones de línea de comandos SIGN_MODE="" for arg in "$@"; do case $arg in --sign) SIGN_MODE="all" shift ;; --sign-chrome) SIGN_MODE="chrome" shift ;; --sign-firefox) SIGN_MODE="firefox" shift ;; --list|-l) LIST_CONTENTS=true shift ;; *) ;; esac done # Archivos a incluir (relativos al directorio raíz) INCLUDE_FILES=( "manifest.json" "background.js" "content/" "skin/" ) # Archivos a excluir (patrones de exclusión) EXCLUDE_PATTERNS=( "*.git*" "*~" "*.swp" ".DS_Store" "Thumbs.db" "*.tmp" "*.log" "*.md" # Excluir documentación (opcional: comentar para incluir) "install.rdf" # Legacy "chrome.manifest" # Legacy "bootstrap.js" # Legacy "update.xml" # Legacy "content/db.js" # Legacy (usamos db-webext.js) "package.json" "package-lock.json" "node_modules/" "dist/" ) # Función para imprimir con color print_status() { echo -e "${GREEN}[✓]${NC} $1" } print_warning() { echo -e "${YELLOW}[!]${NC} $1" } print_error() { echo -e "${RED}[✗]${NC} $1" } # Verificar que estamos en el directorio correcto if [ ! -f "manifest.json" ]; then print_error "No se encuentra manifest.json" print_error "Ejecuta este script desde el directorio raíz del proyecto" exit 1 fi print_status "Directorio de proyecto detectado" # Crear directorio de distribución if [ -d "$DIST_DIR" ]; then print_warning "Limpiando directorio de distribución existente..." rm -rf "$DIST_DIR" fi mkdir -p "$DIST_DIR" print_status "Directorio de distribución creado: $DIST_DIR/" # Función para construir el comando de exclusión para zip build_exclude_args() { local args="" for pattern in "${EXCLUDE_PATTERNS[@]}"; do args="$args -x '$pattern'" done echo "$args" } # Construir paquete para Firefox (.xpi) echo "" echo "Construyendo paquete para Firefox..." print_status "Empaquetando archivos..." # Crear archivo zip temporal TMP_ZIP="$DIST_DIR/temp.zip" # Comando de zip con exclusiones zip -r "$TMP_ZIP" "${INCLUDE_FILES[@]}" \ -x "${EXCLUDE_PATTERNS[@]}" \ > /dev/null 2>&1 # Renombrar a .xpi mv "$TMP_ZIP" "$DIST_DIR/$FIREFOX_PACKAGE" if [ -f "$DIST_DIR/$FIREFOX_PACKAGE" ]; then FIREFOX_SIZE=$(du -h "$DIST_DIR/$FIREFOX_PACKAGE" | cut -f1) print_status "Paquete Firefox creado: $FIREFOX_PACKAGE ($FIREFOX_SIZE)" else print_error "Error al crear paquete Firefox" exit 1 fi # Construir paquete para Chrome (.zip) echo "" echo "Construyendo paquete para Chrome..." print_status "Empaquetando archivos..." zip -r "$DIST_DIR/$CHROME_PACKAGE" "${INCLUDE_FILES[@]}" \ -x "${EXCLUDE_PATTERNS[@]}" \ > /dev/null 2>&1 if [ -f "$DIST_DIR/$CHROME_PACKAGE" ]; then CHROME_SIZE=$(du -h "$DIST_DIR/$CHROME_PACKAGE" | cut -f1) print_status "Paquete Chrome creado: $CHROME_PACKAGE ($CHROME_SIZE)" else print_error "Error al crear paquete Chrome" exit 1 fi # Verificar integridad de los paquetes echo "" print_status "Verificando paquetes..." # Verificar Firefox if unzip -t "$DIST_DIR/$FIREFOX_PACKAGE" > /dev/null 2>&1; then print_status "Paquete Firefox verificado (integridad OK)" else print_error "Paquete Firefox corrupto" exit 1 fi # Verificar Chrome if unzip -t "$DIST_DIR/$CHROME_PACKAGE" > /dev/null 2>&1; then print_status "Paquete Chrome verificado (integridad OK)" else print_error "Paquete Chrome corrupto" exit 1 fi # Firma de paquetes si se solicitó if [ "$SIGN_MODE" = "firefox" ] || [ "$SIGN_MODE" = "all" ]; then echo "" echo "==================================================" echo " Firmando paquete Firefox" echo "==================================================" if [ ! -f "$FIREFOX_JWT" ]; then print_error "No se encontraron credenciales de Firefox: $FIREFOX_JWT" print_warning "Ejecuta: ./scripts/generate-keys.sh" exit 1 fi # Buscar web-ext: primero en node_modules, luego global WEB_EXT="" if [ -f "node_modules/.bin/web-ext" ]; then WEB_EXT="./node_modules/.bin/web-ext" print_status "Usando web-ext local: $WEB_EXT" elif command -v npx &> /dev/null; then WEB_EXT="npx web-ext" print_status "Usando web-ext via npx" elif command -v web-ext &> /dev/null; then WEB_EXT="web-ext" print_status "Usando web-ext global" else print_error "web-ext no está instalado" print_warning "Instalar con: npm install" print_warning "O globalmente: npm install -g web-ext" exit 1 fi print_status "Firmando con AMO..." # Leer credenciales del JSON API_KEY=$(grep -o '"apiKey"[[:space:]]*:[[:space:]]*"[^"]*"' "$FIREFOX_JWT" | cut -d'"' -f4) API_SECRET=$(grep -o '"apiSecret"[[:space:]]*:[[:space:]]*"[^"]*"' "$FIREFOX_JWT" | cut -d'"' -f4) if [ -z "$API_KEY" ] || [ -z "$API_SECRET" ]; then print_error "Credenciales de AMO inválidas en $FIREFOX_JWT" exit 1 fi # Firmar con web-ext # Nota: El ID se lee automáticamente de manifest.json, no se pasa como parámetro $WEB_EXT sign \ --source-dir=. \ --artifacts-dir="$DIST_DIR" \ --api-key="$API_KEY" \ --api-secret="$API_SECRET" \ --channel=unlisted \ 2>&1 | tee "$DIST_DIR/firefox-sign.log" # Verificar el código de salida SIGN_EXIT_CODE=${PIPESTATUS[0]} if [ $SIGN_EXIT_CODE -eq 0 ]; then print_status "Paquete Firefox firmado exitosamente" # Buscar el archivo firmado SIGNED_XPI=$(ls -t "$DIST_DIR"/*.xpi 2>/dev/null | head -1) if [ -f "$SIGNED_XPI" ]; then print_status "Archivo firmado: $(basename "$SIGNED_XPI")" fi else print_error "Error al firmar paquete Firefox (código: $SIGN_EXIT_CODE)" print_warning "Revisa: $DIST_DIR/firefox-sign.log" exit $SIGN_EXIT_CODE fi fi if [ "$SIGN_MODE" = "chrome" ] || [ "$SIGN_MODE" = "all" ]; then echo "" echo "==================================================" echo " Firmando paquete Chrome" echo "==================================================" if [ ! -f "$CHROME_KEY" ]; then print_error "No se encontró clave de Chrome: $CHROME_KEY" print_warning "Ejecuta: ./scripts/generate-keys.sh" exit 1 fi if ! command -v google-chrome &> /dev/null && ! command -v chromium &> /dev/null; then print_warning "Chrome/Chromium no encontrado, usando método alternativo..." # Método alternativo: crear .crx manualmente print_status "Generando .crx con openssl..." # Extraer clave pública openssl rsa -in "$CHROME_KEY" -pubout -outform DER > "$DIST_DIR/public.der" 2>/dev/null # Crear firma openssl dgst -sha256 -sign "$CHROME_KEY" -out "$DIST_DIR/signature.bin" "$DIST_DIR/$CHROME_PACKAGE" # Leer tamaños pub_size=$(wc -c < "$DIST_DIR/public.der") sig_size=$(wc -c < "$DIST_DIR/signature.bin") # Crear header CRX3 ( printf "Cr24" # Magic number printf '\x03\x00\x00\x00' # Version 3 printf "$(printf '%08x' $pub_size | sed 's/\(..\)/\\x\1/g' | tac -s'\\')" # Public key length (little endian) printf "$(printf '%08x' $sig_size | sed 's/\(..\)/\\x\1/g' | tac -s'\\')" # Signature length (little endian) cat "$DIST_DIR/public.der" cat "$DIST_DIR/signature.bin" cat "$DIST_DIR/$CHROME_PACKAGE" ) > "$DIST_DIR/$CHROME_CRX" # Limpiar archivos temporales rm -f "$DIST_DIR/public.der" "$DIST_DIR/signature.bin" if [ -f "$DIST_DIR/$CHROME_CRX" ]; then CRX_SIZE=$(du -h "$DIST_DIR/$CHROME_CRX" | cut -f1) print_status "Paquete Chrome firmado: $CHROME_CRX ($CRX_SIZE)" # Calcular hash CRX_HASH=$(sha256sum "$DIST_DIR/$CHROME_CRX" | cut -d' ' -f1) echo "$CRX_HASH" > "$DIST_DIR/${CHROME_CRX}.sha256" print_status "Hash SHA256 guardado" else print_error "Error al crear .crx" fi else print_status "Empaquetando con Chrome..." # Usar Chrome para empaquetar CHROME_BIN=$(command -v google-chrome || command -v chromium) "$CHROME_BIN" --pack-extension=. --pack-extension-key="$CHROME_KEY" --no-message-box 2>/dev/null if [ -f "$(basename $(pwd)).crx" ]; then mv "$(basename $(pwd)).crx" "$DIST_DIR/$CHROME_CRX" print_status "Paquete Chrome firmado: $CHROME_CRX" else print_error "Error al firmar con Chrome" fi fi # Calcular Extension ID if [ -f "$CHROME_KEY" ]; then CHROME_ID=$(openssl rsa -in "$CHROME_KEY" -pubout -outform DER 2>/dev/null | \ openssl dgst -sha256 -binary | \ head -c 16 | \ od -An -tx1 | \ tr -d ' \n' | \ tr '0-9a-f' 'a-p') echo "$CHROME_ID" > "$DIST_DIR/chrome-extension-id.txt" print_status "Extension ID: $CHROME_ID" fi fi # Listar contenido (opcional) if [ "$LIST_CONTENTS" = true ]; then echo "" echo "Contenido del paquete Firefox:" unzip -l "$DIST_DIR/$FIREFOX_PACKAGE" fi # Resumen final echo "" echo "==================================================" echo " ✨ Build completado exitosamente" echo "==================================================" echo "" echo "Paquetes generados en: $DIST_DIR/" echo "" echo " Firefox: $FIREFOX_PACKAGE ($FIREFOX_SIZE)" echo " Chrome: $CHROME_PACKAGE ($CHROME_SIZE)" if [ "$SIGN_MODE" = "chrome" ] || [ "$SIGN_MODE" = "all" ]; then if [ -f "$DIST_DIR/$CHROME_CRX" ]; then CRX_SIZE=$(du -h "$DIST_DIR/$CHROME_CRX" | cut -f1) echo " Chrome (firmado): $CHROME_CRX ($CRX_SIZE)" fi fi echo "" if [ -z "$SIGN_MODE" ]; then echo " ℹ️ Paquetes sin firmar. Para firmar usa:" echo " ./build.sh --sign # Firmar ambos" echo " ./build.sh --sign-firefox # Solo Firefox" echo " ./build.sh --sign-chrome # Solo Chrome" echo "" fi echo "Próximos pasos:" echo "" if [ -n "$SIGN_MODE" ]; then echo " Paquetes firmados ✓" echo "" echo " Para publicar:" if [ "$SIGN_MODE" = "firefox" ] || [ "$SIGN_MODE" = "all" ]; then echo " Firefox AMO: El archivo firmado está listo para subir" echo " https://addons.mozilla.org/developers/" fi if [ "$SIGN_MODE" = "chrome" ] || [ "$SIGN_MODE" = "all" ]; then echo " Chrome Web Store: Sube el archivo .zip (no .crx)" echo " https://chrome.google.com/webstore/devconsole" fi else echo " Firefox:" echo " 1. Ir a about:debugging#/runtime/this-firefox" echo " 2. Click en 'Cargar complemento temporal'" echo " 3. Seleccionar: $DIST_DIR/$FIREFOX_PACKAGE" echo "" echo " Chrome:" echo " 1. Ir a chrome://extensions/" echo " 2. Activar 'Modo de desarrollador'" echo " 3. Click en 'Cargar extensión sin empaquetar'" echo " 4. Extraer y seleccionar la carpeta de $CHROME_PACKAGE" fi echo "" echo "==================================================" exit 0