#!/bin/zsh set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" PROJECT_PATH="${PROJECT_PATH:-$ROOT_DIR/PortfolioJournal.xcodeproj}" SCHEME="${SCHEME:-PortfolioJournal}" CONFIGURATION="${CONFIGURATION:-Release}" ARCHIVE_DIR="${ARCHIVE_DIR:-$ROOT_DIR/build/appstore}" ARCHIVE_PATH="${ARCHIVE_PATH:-$ARCHIVE_DIR/${SCHEME}.xcarchive}" EXPORT_PATH="${EXPORT_PATH:-$ARCHIVE_DIR/export}" EXPORT_OPTIONS_PLIST="${EXPORT_OPTIONS_PLIST:-$ARCHIVE_DIR/ExportOptions.plist}" # Credenciales desde pass (GPG store). Se puede sobreescribir con variables de entorno. _pass_or_env() { local env_val="$1" pass_key="$2" if [[ -n "$env_val" ]]; then echo "$env_val" elif command -v pass >/dev/null 2>&1; then pass show "$pass_key" 2>/dev/null || fail "No se encontro la credencial en pass: $pass_key" else fail "pass no esta disponible y la variable de entorno no esta definida para: $pass_key" fi } KEY_ID="$(_pass_or_env "${APPSTORE_KEY_ID:-}" appstore/api-key-id)" ISSUER_ID="$(_pass_or_env "${APPSTORE_ISSUER_ID:-}" appstore/issuer-id)" TEAM_ID="$(_pass_or_env "${DEVELOPMENT_TEAM_ID:-}" appstore/team-id)" P8_PATH="${APPSTORE_P8_PATH:-}" P8_DIR="" fail() { echo "Error: $*" >&2 exit 1 } # Resuelve la clave .p8 y la deja en la variable global P8_PATH. # IMPORTANTE: NO llamar con $(find_p8_file). El fichero temporal extraído de pass se # limpia con un `trap EXIT`; si la función corre dentro de un subshell de sustitución # de comandos, ese trap se dispara al cerrarse el subshell y borra el .p8 ANTES del # export (xcodebuild fallaba con "-authenticationKeyPath ... existing file"). find_p8_file() { # 1. Variable de entorno explícita if [[ -n "$P8_PATH" ]]; then [[ -f "$P8_PATH" ]] || fail "No existe el fichero APPSTORE_P8_PATH=$P8_PATH" return fi # 2. Extraer de pass y escribir en fichero temporal (limpieza al salir del script) if command -v pass >/dev/null 2>&1 && pass show appstore/api-key-p8 &>/dev/null; then local tmp_p8 tmp_p8="$(mktemp /tmp/AuthKey_XXXXXX.p8)" pass show appstore/api-key-p8 > "$tmp_p8" chmod 600 "$tmp_p8" # Registrar para limpieza al salir del script (trap en el shell principal). trap "rm -f '$tmp_p8'" EXIT P8_PATH="$tmp_p8" return fi # 3. Fichero por defecto en HOME local default_path="$HOME/AuthKey_${KEY_ID}.p8" if [[ -f "$default_path" ]]; then P8_PATH="$default_path" return fi # 4. Único .p8 en HOME local matches=("${HOME}"/*.p8(N)) if (( ${#matches[@]} == 1 )); then P8_PATH="${matches[1]}" return fi if (( ${#matches[@]} > 1 )); then fail "Hay varios ficheros .p8 en $HOME. Define APPSTORE_P8_PATH o inserta la clave en pass: appstore/api-key-p8" fi fail "No se encontro la clave .p8. Insértala con: cat AuthKey.p8 | pass insert -f -e appstore/api-key-p8" } create_export_options() { mkdir -p "$ARCHIVE_DIR" cat > "$EXPORT_OPTIONS_PLIST" < destination export manageAppVersionAndBuildNumber method app-store-connect signingStyle automatic stripSwiftSymbols teamID ${TEAM_ID} uploadSymbols EOF } archive_app() { echo "==> Archivando ${SCHEME}" xcodebuild \ -project "$PROJECT_PATH" \ -scheme "$SCHEME" \ -configuration "$CONFIGURATION" \ -archivePath "$ARCHIVE_PATH" \ -destination "generic/platform=iOS" \ clean archive } export_ipa() { echo "==> Exportando IPA" xcodebuild \ -exportArchive \ -archivePath "$ARCHIVE_PATH" \ -exportPath "$EXPORT_PATH" \ -exportOptionsPlist "$EXPORT_OPTIONS_PLIST" \ -allowProvisioningUpdates \ -authenticationKeyPath "$P8_PATH" \ -authenticationKeyID "$KEY_ID" \ -authenticationKeyIssuerID "$ISSUER_ID" } upload_ipa() { local ipa ipa="$(find "$EXPORT_PATH" -maxdepth 1 -name '*.ipa' -print -quit)" [[ -n "$ipa" ]] || fail "No se encontro ningun .ipa en $EXPORT_PATH" echo "==> Subiendo $(basename "$ipa") a App Store Connect" API_PRIVATE_KEYS_DIR="$P8_DIR" \ xcrun altool \ --upload-app \ --type ios \ --file "$ipa" \ --apiKey "$KEY_ID" \ --apiIssuer "$ISSUER_ID" } main() { command -v xcodebuild >/dev/null 2>&1 || fail "xcodebuild no esta disponible" command -v xcrun >/dev/null 2>&1 || fail "xcrun no esta disponible" find_p8_file # asigna P8_PATH; NO usar $(...) o el trap del subshell borra el .p8 P8_DIR="$(dirname "$P8_PATH")" echo "Usando proyecto: $PROJECT_PATH" echo "Usando scheme: $SCHEME" echo "Usando clave API: $KEY_ID" echo "Usando fichero .p8: $P8_PATH" rm -rf "$ARCHIVE_PATH" "$EXPORT_PATH" create_export_options archive_app export_ipa upload_ipa echo "==> Proceso completado" } main "$@"