From 5bb17909b9147498069397249ec315495c1cee71 Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Sat, 23 May 2026 14:09:42 -0400 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20nuevo=20script=20y=20configuraci?= =?UTF-8?q?=C3=B3n=20keystore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Se agrega script para generar keystore * Se configura android para usar variables de entorno de keystore o en su defecto el local.properties * Se agrega información de compilación en Android en README --- README.md | 136 ++++++++++------- android/app/build.gradle.kts | 13 +- android/fastlane/Appfile | 2 +- scripts/generate-keystore | 273 +++++++++++++++++++++++++++++++++++ 4 files changed, 367 insertions(+), 57 deletions(-) create mode 100755 scripts/generate-keystore diff --git a/README.md b/README.md index 287c25a..f25c308 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ # Mi UTEM para Android e iOS + Aplicación multiplataforma hecha por estudiantes de la [Universidad Tecnológica Metropolitana de Chile](https://www.utem.cl/) enfocada en adaptar la [plataforma académica Mi.UTEM](https://mi.utem.cl/) de la institución a dispositivos móviles. ## Requisitos técnicos + - Flutter 3.16.3 o superior. - Dart 3.2.3 o superior. - Cualquier IDE compatible con Flutter (Android Studio, VS Code, IDEA, etc). - Un dispositivo Android o iOS para probar la aplicación. (Para iOS se requiere un Mac, también puedes usar el simulador de iOS y Android). -## Configuración de librerías y dependencias +## Configuración de librerías y dependencias
@@ -47,10 +49,11 @@ Aplicación multiplataforma hecha por estudiantes de la [Universidad Tecnológic source $(brew --prefix)/opt/chruby/share/chruby/auto.sh chruby ruby-4.0.1 # Reemplaza con la versión de ruby que instalaste ``` - 3. Después de configurar `chruby`, puedes instalar la versión de ruby que necesitas (recomendamos usar la última versión estable) con el siguiente comando: - ```bash - ruby-install -U ruby - ``` +3. Después de configurar `chruby`, puedes instalar la versión de ruby que necesitas (recomendamos usar la última versión estable) con el siguiente comando: + +```bash +ruby-install -U ruby +```
@@ -71,78 +74,81 @@ Aplicación multiplataforma hecha por estudiantes de la [Universidad Tecnológic ## Configuración del Proyecto + > Se asume que ya tienes instalado Flutter, Dart, y las herramientas necesarias para ejecutar aplicaciones Flutter en tu dispositivo como XCode y/o Android Studio. + 1. Clona el repositorio en tu computador. 2. Abre el proyecto en un terminal y ejecuta `flutter pub get` para instalar las dependencias. 3. Si ejecutarás la app en un dispositivo Apple en iOS o macOS deberás de ingresar a su carpeta correspondiente y ejecutar `pod install --repo-update` para instalar las dependencias de CocoaPods. 4. Una vez que hayas instalado las dependencias, configura firebase usando `flutterfire`. Sigue las instrucciones de `firebase` ## Configuración de Firebase usando `flutterfire` + 1. Para configurar firebase usando `flutterfire`, primero debes ejecutar el siguiente comando en la raíz del proyecto: ```bash ./scripts/flutterfire-configure all # Puedes agregar --dry-run para ver que comandos se ejecutarán. ``` 2. Se te preguntará que plataformas quieres configurar. Primero deberá ir las de desarrollo. Puedes validar esto ya que saldrá en el output el proyecto de firebase. En este caso es miutem-dev ``` - i Found 7 Firebase projects. Selecting project miutem-dev. <------ Este es el proyecto de firebase. - ? Which platforms should your configuration support (use arrow keys & space to select)? › - ✔ android - ✔ ios + i Found 7 Firebase projects. Selecting project miutem-dev. <------ Este es el proyecto de firebase. + ? Which platforms should your configuration support (use arrow keys & space to select)? › + ✔ android + ✔ ios ✔ macos web windows - ``` - Asegúrate de seleccionar solo las plataformas de desarrollo actual (android, ios, macos). + ``` + Asegúrate de seleccionar solo las plataformas de desarrollo actual (android, ios, macos). 3. Luego debes seleccionar `Build Configuration` como tipo de configuración ``` i Found 7 Firebase projects. Selecting project miutem-dev. - ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos - ? You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. › - ❯ Build configuration - Target + ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos + ? You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. › + ❯ Build configuration + Target ``` 4. Luego debes seleccionar `Release-development` como tipo de build configuration ``` - i Found 7 Firebase projects. Selecting project miutem-dev. - ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos - ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration - ? Please choose one of the following build configurations › - Debug - Debug-production - Debug-development - Release - Release-production - ❯ Release-development - Profile - Profile-production + i Found 7 Firebase projects. Selecting project miutem-dev. + ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos + ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration + ? Please choose one of the following build configurations › + Debug + Debug-production + Debug-development + Release + Release-production + ❯ Release-development + Profile + Profile-production Profile-development ``` 5. Ahora deberás seleccionar y configurar `Build Configuration`, esta vez es para la plataforma de macOS ``` - i Found 7 Firebase projects. Selecting project miutem-dev. - ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos - ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration - ✔ Please choose one of the following build configurations · Release-development - ? You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. › - ❯ Build configuration + i Found 7 Firebase projects. Selecting project miutem-dev. + ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos + ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration + ✔ Please choose one of the following build configurations · Release-development + ? You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. › + ❯ Build configuration Target ``` 6. Nuevamente debes seleccionar `Release-development` como tipo de build configuration para macOS ``` - i Found 7 Firebase projects. Selecting project miutem-dev. - ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos - ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration - ✔ Please choose one of the following build configurations · Release-development - ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration - ? Please choose one of the following build configurations › - Debug - Debug-production - Debug-development - Release - ❯ Release-development - Release-production - Profile - Profile-production + i Found 7 Firebase projects. Selecting project miutem-dev. + ✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, macos + ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration + ✔ Please choose one of the following build configurations · Release-development + ✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration + ? Please choose one of the following build configurations › + Debug + Debug-production + Debug-development + Release + ❯ Release-development + Release-production + Profile + Profile-production Profile-development ``` 7. Luego deberás repetir los pasos para configurar producción, pero esta vez seleccionando `Release-production` como tipo de build configuration para cada plataforma. Esto es importante ya que el proyecto de firebase tiene configuraciones separadas para desarrollo y producción. @@ -151,37 +157,63 @@ Aplicación multiplataforma hecha por estudiantes de la [Universidad Tecnológic [x] FlutterFire: "flutterfire bundle-service-file" [x] FlutterFire: "flutterfire upload-crashlytics-symbols" ``` -Si no revisas el orden tendrás problemas al compilar ya que el comando para subir los símbolos de crashlytics se ejecutará antes de generar el archivo de configuración de firebase, lo que hará que el comando falle ya que no encontrará el archivo de configuración. -Mas información del problema la puedes encontrar en la siguiente discusión: [[BUG]: La app no compila por un problema de flutterfire. #29](https://github.com/exdevutem/MiUTEM/discussions/29) + Si no revisas el orden tendrás problemas al compilar ya que el comando para subir los símbolos de crashlytics se ejecutará antes de generar el archivo de configuración de firebase, lo que hará que el comando falle ya que no encontrará el archivo de configuración. + Mas información del problema la puedes encontrar en la siguiente discusión: [[BUG]: La app no compila por un problema de flutterfire. #29](https://github.com/exdevutem/MiUTEM/discussions/29) > **IMPORTANTE**: Si te pierdes o eliges mal alguna configuración no hay problema, puedes volver a ejecutar el comando `flutterfire configure` para corregir cualquier error o configuración mal hecha. También puedes revisar el script `scripts/flutterfire-configure` para entender mejor como funciona la configuración de firebase usando `flutterfire`. -> +> > También, si el script te pregunta para reemplazar el archivo `firebase_options_.dart` puedes elegir `yes` ya que el script se encarga de generar el archivo con la configuración correcta para cada entorno. No te preocupes por perder alguna configuración personalizada que hayas hecho en el archivo, ya que el script solo genera la configuración de firebase y no toca ninguna otra parte del código. +> > ``` -> ? Generated FirebaseOptions file .../lib/firebase_options_prod.dart already exists, do you want to override it? · yes +> ? Generated FirebaseOptions file .../lib/firebase_options_prod.dart already exists, do you want to override it? · yes > ```
+## Compilación para Android + +Para compilar la aplicación para Android, primero debes asegurarte de tener configurado un dispositivo Android o un emulador. Una vez que tengas eso listo, debes configurar el keystore para firmar la aplicación. Para esto, debes generar un keystore usando el siguiente comando: + +```bash +# Sigue las instrucciones para generar el keystore +./scripts/generate-keystore +``` + +Una vez configurado el keystore y las variables, puedes compilar la aplicación para Android usando el siguiente comando: +En producción: + +```bash +flutter build apk --flavor production -t lib/main_prod.dart +``` + +En desarrollo: + +```bash +flutter build apk --flavor development -t lib/main_dev.dart +``` + ## Ejecución de la aplicación + Una vez que hayas configurado firebase correctamente, puedes ejecutar la aplicación en tu dispositivo usando el siguiente comando: En producción: + ```bash flutter run --flavor production -t lib/main_prod.dart ``` En desarrollo: + ```bash flutter run --flavor development -t lib/main_dev.dart ``` - Si quieres ejecutar la app en un IDE como Android Studio, IntelliJ IDEA o VSCode, puedes usar las configuraciones de ejecución que ya existen para cada entorno. Solo asegúrate de seleccionar la configuración correcta para el entorno que quieres ejecutar (producción o desarrollo). Para el entorno de desarrollo está la configración `Mi UTEM [development]` y para el entorno de producción está la configuración `Mi UTEM [production]`. Estas configuraciones ya están predefinidas para ejecutar la aplicación con el comando correcto para cada entorno. ## Créditos + Este proyecto fue creado por el Club de Desarrollo Experimental (ExDev) de la Universidad Tecnológica Metropolitana y es mantenido por los propios estudiantes con el apoyo del equipo de SISEI. Mira los perfiles que han contribuido a este proyecto: diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index d63d899..06accab 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -35,12 +35,17 @@ android { keystoreProperties.load(FileInputStream(keystorePropertiesFile)) } + val resolvedStoreFile = System.getenv("MIUTEM_KEYSTORE_PATH") ?: keystoreProperties["storeFile"] as String? + val resolvedStorePassword = System.getenv("MIUTEM_KEYSTORE_PASSWORD") ?: keystoreProperties["storePassword"] as String? + val resolvedKeyAlias = System.getenv("MIUTEM_KEY_ALIAS") ?: keystoreProperties["keyAlias"] as String? + val resolvedKeyPassword = System.getenv("MIUTEM_KEY_PASSWORD") ?: keystoreProperties["keyPassword"] as String? + signingConfigs { create("release") { - keyAlias = keystoreProperties["keyAlias"] as String - keyPassword = keystoreProperties["keyPassword"] as String - storeFile = keystoreProperties["storeFile"]?.let { file(it) } - storePassword = keystoreProperties["storePassword"] as String + keyAlias = resolvedKeyAlias + keyPassword = resolvedKeyPassword + storeFile = resolvedStoreFile?.let { file(it) } + storePassword = resolvedStorePassword } } diff --git a/android/fastlane/Appfile b/android/fastlane/Appfile index bd46386..b399205 100644 --- a/android/fastlane/Appfile +++ b/android/fastlane/Appfile @@ -1,2 +1,2 @@ json_key_file("api-playstore.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one -package_name("cl.inndev.miutem") # e.g. com.krausefx.app +package_name(ENV["APP_PACKAGE_NAME"]) diff --git a/scripts/generate-keystore b/scripts/generate-keystore new file mode 100755 index 0000000..69fdb1b --- /dev/null +++ b/scripts/generate-keystore @@ -0,0 +1,273 @@ +#!/bin/bash +################################################## +# Script para generar un keystore de Android +# Autor: Francisco Solis Maturana (Club de Desarrollo Experimental) +# Uso: ./generate-keystore [flags] +# Flags: +# -h, --help Muestra esta ayuda +# -v, --verbose Muestra información detallada durante la ejecución +# --dry-run Muestra los comandos que se ejecutarían sin ejecutarlos +# --keystore_location= Ubicación del keystore (por defecto: ~/.keys/exdevutem) +# --keystore_name= Nombre del archivo keystore (por defecto: miutem.jks) +# --keystore_password= Contraseña del keystore +# --key_password= Contraseña de la llave +# --key_alias= Alias de la llave (por defecto: upload) +# --developer_name= Nombre completo del desarrollador +# --developer_organization= Organización (por defecto: Club de Desarrollo Experimental) +# --developer_unit= Unidad organizacional (por defecto: Desarrolladores) +# --developer_city= Ciudad (por defecto: Santiago) +# --developer_region= Región (por defecto: Metropolitana) +# --developer_country= País en código ISO (por defecto: CL) +################################################## + +VERBOSE=false +DRY_RUN=false + +KEYSTORE_LOCATION="" +KEYSTORE_NAME="" +KEYSTORE_PASSWORD="" +KEY_PASSWORD="" +KEY_ALIAS="" +DEVELOPER_NAME="" +DEVELOPER_ORGANIZATION="" +DEVELOPER_UNIT="" +DEVELOPER_CITY="" +DEVELOPER_REGION="" +DEVELOPER_COUNTRY="" + +print_help() { + cat <<'EOF' +Uso: ./generate-keystore [flags] + +Flags: + -h, --help Muestra esta ayuda + -v, --verbose Muestra información detallada durante la ejecución + --dry-run Muestra los comandos sin ejecutarlos + + --keystore_location= Ubicación del keystore (por defecto: ~/.keys/exdevutem) + --keystore_name= Nombre del archivo keystore (por defecto: miutem.jks) + --keystore_password= Contraseña del keystore + --key_password= Contraseña de la llave + --key_alias= Alias de la llave (por defecto: upload) + --developer_name= Nombre completo del desarrollador + --developer_organization= Organización (por defecto: Club de Desarrollo Experimental) + --developer_unit= Unidad organizacional (por defecto: Desarrolladores) + --developer_city= Ciudad (por defecto: Santiago) + --developer_region= Región (por defecto: Metropolitana) + --developer_country= País en código ISO (por defecto: CL) +EOF +} + +log_verbose() { + if [ "$VERBOSE" = true ]; then + echo "[verbose] $1" + fi +} + +run_cmd() { + local cmd=("$@") + + if [ "$VERBOSE" = true ] || [ "$DRY_RUN" = true ]; then + echo "[cmd] ${cmd[*]}" + fi + + if [ "$DRY_RUN" = true ]; then + return 0 + fi + + "${cmd[@]}" +} + +while [ $# -gt 0 ]; do + case "$1" in + -h|--help) + print_help + exit 0 + ;; + -v|--verbose) + VERBOSE=true + ;; + --dry-run) + DRY_RUN=true + ;; + --keystore_location=*) + KEYSTORE_LOCATION="${1#*=}" + ;; + --keystore_name=*) + KEYSTORE_NAME="${1#*=}" + ;; + --keystore_password=*) + KEYSTORE_PASSWORD="${1#*=}" + ;; + --key_password=*) + KEY_PASSWORD="${1#*=}" + ;; + --key_alias=*) + KEY_ALIAS="${1#*=}" + ;; + --developer_name=*) + DEVELOPER_NAME="${1#*=}" + ;; + --developer_organization=*) + DEVELOPER_ORGANIZATION="${1#*=}" + ;; + --developer_unit=*) + DEVELOPER_UNIT="${1#*=}" + ;; + --developer_city=*) + DEVELOPER_CITY="${1#*=}" + ;; + --developer_region=*) + DEVELOPER_REGION="${1#*=}" + ;; + --developer_country=*) + DEVELOPER_COUNTRY="${1#*=}" + ;; + *) + echo "Error: argumento no reconocido '$1'." + print_help + exit 1 + ;; + esac + shift +done + +if ! command -v keytool &> /dev/null; then + echo "Error: el comando keytool no está disponible. Por favor, instale Java Development Kit (JDK) para obtener acceso a keytool." + exit 1 +fi + +if [ -z "$KEYSTORE_LOCATION" ]; then + read -p "Ingrese la ubicación del keystore [$HOME/.keys/exdevutem]: " KEYSTORE_LOCATION + KEYSTORE_LOCATION="${KEYSTORE_LOCATION:-$HOME/.keys/exdevutem}" +fi + +if [ -z "$KEYSTORE_NAME" ]; then + read -p "Ingrese el nombre del keystore [miutem.jks]: " KEYSTORE_NAME + KEYSTORE_NAME="${KEYSTORE_NAME:-miutem.jks}" +fi + +if [ -z "$KEYSTORE_PASSWORD" ]; then + read -s -p "Ingrese la contraseña del keystore: " KEYSTORE_PASSWORD + echo +fi + +if [ -z "$KEY_PASSWORD" ]; then + read -s -p "Ingrese la contraseña de la llave: " KEY_PASSWORD + echo +fi + +if [ -z "$KEY_ALIAS" ]; then + read -p "Ingrese el alias de la llave [upload]: " KEY_ALIAS + KEY_ALIAS="${KEY_ALIAS:-upload}" +fi + +if [ -z "$DEVELOPER_NAME" ]; then + read -p "Ingrese el nombre completo del desarrollador: " DEVELOPER_NAME +fi + +if [ -z "$DEVELOPER_ORGANIZATION" ]; then + read -p "Ingrese la organización del desarrollador [Club de Desarrollo Experimental]: " DEVELOPER_ORGANIZATION + DEVELOPER_ORGANIZATION="${DEVELOPER_ORGANIZATION:-Club de Desarrollo Experimental}" +fi + +if [ -z "$DEVELOPER_UNIT" ]; then + read -p "Ingrese la unidad organizacional del desarrollador [Desarrolladores]: " DEVELOPER_UNIT + DEVELOPER_UNIT="${DEVELOPER_UNIT:-Desarrolladores}" +fi + +if [ -z "$DEVELOPER_CITY" ]; then + read -p "Ingrese la ciudad del desarrollador [Santiago]: " DEVELOPER_CITY + DEVELOPER_CITY="${DEVELOPER_CITY:-Santiago}" +fi + +if [ -z "$DEVELOPER_REGION" ]; then + read -p "Ingrese la región del desarrollador [Metropolitana]: " DEVELOPER_REGION + DEVELOPER_REGION="${DEVELOPER_REGION:-Metropolitana}" +fi + +if [ -z "$DEVELOPER_COUNTRY" ]; then + read -p "Ingrese el país del desarrollador [CL]: " DEVELOPER_COUNTRY + DEVELOPER_COUNTRY="${DEVELOPER_COUNTRY:-CL}" +fi + +log_verbose "Ubicación del keystore: $KEYSTORE_LOCATION" +log_verbose "Nombre del keystore: $KEYSTORE_NAME" +log_verbose "Alias de la llave: $KEY_ALIAS" +log_verbose "Desarrollador: $DEVELOPER_NAME" +log_verbose "Organización: $DEVELOPER_ORGANIZATION" +log_verbose "Unidad organizacional: $DEVELOPER_UNIT" +log_verbose "Ciudad: $DEVELOPER_CITY" +log_verbose "Región: $DEVELOPER_REGION" +log_verbose "País: $DEVELOPER_COUNTRY" + +if [ ! -d "$KEYSTORE_LOCATION" ]; then + log_verbose "Creando directorio: $KEYSTORE_LOCATION" + run_cmd mkdir -p "$KEYSTORE_LOCATION" +fi + +echo "================================" +echo "Generando keystore: $KEYSTORE_LOCATION/$KEYSTORE_NAME" +echo "================================" + +# Revisa si ya existe un keystore con el mismo nombre +if [ -f "$KEYSTORE_LOCATION/$KEYSTORE_NAME" ]; then + read -p "El keystore ya existe. ¿Desea sobrescribirlo? (s/n) " confirm + if [[ "$confirm" != "s" ]]; then + echo "Operación cancelada. No se ha generado el keystore." + exit 0 + fi +fi + +# Borra el keystore existente si el usuario confirmó sobrescribirlo +if [ -f "$KEYSTORE_LOCATION/$KEYSTORE_NAME" ]; then + log_verbose "Eliminando keystore existente: $KEYSTORE_LOCATION/$KEYSTORE_NAME" + run_cmd rm -f "$KEYSTORE_LOCATION/$KEYSTORE_NAME" +fi + +run_cmd keytool -genkey -v \ + -keystore "$KEYSTORE_LOCATION/$KEYSTORE_NAME" \ + -keyalg RSA \ + -keysize 2048 \ + -validity 10000 \ + -dname "CN=$DEVELOPER_NAME, OU=$DEVELOPER_UNIT, O=$DEVELOPER_ORGANIZATION, L=$DEVELOPER_CITY, ST=$DEVELOPER_REGION, C=$DEVELOPER_COUNTRY" \ + -storepass "$KEYSTORE_PASSWORD" \ + -keypass "$KEY_PASSWORD" \ + -alias "$KEY_ALIAS" + +echo "Keystore generado exitosamente en: $KEYSTORE_LOCATION/$KEYSTORE_NAME" + +# Ahora debemos configurar en $HOME/.gradle/gradle.properties las siguientes variables: +# MIUTEM_KEYSTORE_PATH=$KEYSTORE_LOCATION/$KEYSTORE_NAME +# MIUTEM_KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD +# MIUTEM_KEY_ALIAS=$KEY_ALIAS +# MIUTEM_KEY_PASSWORD=$KEY_PASSWORD + +echo "Para configurar la firma de tu aplicación, agrega las siguientes líneas a tu archivo ~/.gradle/gradle.properties:" +echo "==============================================================" +echo "MIUTEM_KEYSTORE_PATH=$KEYSTORE_LOCATION/$KEYSTORE_NAME" +echo "MIUTEM_KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" +echo "MIUTEM_KEY_ALIAS=$KEY_ALIAS" +echo "MIUTEM_KEY_PASSWORD=$KEY_PASSWORD" +echo "==============================================================" + +# Preguntar si el usuario desea agregar estas líneas automáticamente al archivo gradle.properties +read -p "¿Desea agregar estas líneas automáticamente a su archivo ~/.gradle/gradle.properties? (s/n) " add_to_gradle +if [[ "$add_to_gradle" == "s" ]]; then + GRADLE_PROPERTIES="$HOME/.gradle/gradle.properties" + if [ ! -f "$GRADLE_PROPERTIES" ]; then + log_verbose "Creando archivo gradle.properties: $GRADLE_PROPERTIES" + run_cmd touch "$GRADLE_PROPERTIES" + fi + { + echo "" + echo "# Configuración de firma para miutem" + echo "MIUTEM_KEYSTORE_PATH=$KEYSTORE_LOCATION/$KEYSTORE_NAME" + echo "MIUTEM_KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" + echo "MIUTEM_KEY_ALIAS=$KEY_ALIAS" + echo "MIUTEM_KEY_PASSWORD=$KEY_PASSWORD" + } >> "$GRADLE_PROPERTIES" + echo "Líneas agregadas exitosamente a $GRADLE_PROPERTIES" +else + echo "No se han agregado las líneas al archivo gradle.properties. Recuerda hacerlo manualmente para configurar la firma de tu aplicación." +fi \ No newline at end of file From a6d890b466eae47893176bce044262e8cc365aa0 Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Sun, 24 May 2026 17:19:33 -0400 Subject: [PATCH 02/10] feat: se modifica fastlane para usar correctamente las plataformas --- .github/workflows/deploy.yml | 2 +- fastlane/.env.example | 13 +- fastlane/Fastfile | 23 +--- fastlane/README.md | 29 +++++ ios/fastlane/Fastfile | 242 ++++++++++++++++++----------------- 5 files changed, 165 insertions(+), 144 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9e651bb..5ccfb86 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -82,7 +82,7 @@ jobs: run: ./scripts/flutterfire-configure dev - name: 🚀 Desplegar a TestFlight - run: bundle exec fastlane ios build_and_upload + run: bundle exec fastlane ios deploy env: IS_CI: ${{ vars.IS_CI }} APP_IDENTIFIER: ${{ secrets.APP_IDENTIFIER }} diff --git a/fastlane/.env.example b/fastlane/.env.example index d080f41..ac7d4cf 100644 --- a/fastlane/.env.example +++ b/fastlane/.env.example @@ -33,4 +33,15 @@ MATCH_GIT_BASIC_AUTHORIZATION= # App Store Connect API Key configuration for iOS app distribution APP_STORE_CONNECT_API_KEY_ID= APP_STORE_CONNECT_ISSUER_ID= -APP_STORE_CONNECT_API_KEY_CONTENT= \ No newline at end of file +APP_STORE_CONNECT_API_KEY_CONTENT= + +# Android keystore configuration +MIUTEM_KEYSTORE_PATH= +MIUTEM_KEYSTORE_PASSWORD= +MIUTEM_KEY_ALIAS= +MIUTEM_KEY_PASSWORD= +MIUTEM_USE_DEBUG_SIGNING=false +# Google Play Service Account +GOOGLE_PLAY_JSON_KEY= +# Track de Google Play (internal, alpha, beta, production) +ANDROID_TRACK=internal \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 8170781..a167a41 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,23 +1,2 @@ import "../ios/fastlane/Fastfile" - -default_platform(:ios) - -platform :ios do - desc "Compila la aplicación para iOS" - lane :build do - ios_build() - end - - desc "Sube la aplicación a TestFlight (requiere IPA)" - lane :upload do |options| - ios_upload( - ipa_path: options[:ipa_path] - ) - end - - desc "Build completo y upload a TestFlight" - lane :build_and_upload do - ios_build() - ios_upload() - end -end \ No newline at end of file +import "../android/fastlane/Fastfile" \ No newline at end of file diff --git a/fastlane/README.md b/fastlane/README.md index 0bce751..f958bf8 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -64,6 +64,35 @@ Build completo y upload a TestFlight ---- +## Android + +### android android_flutter_build + +```sh +[bundle exec] fastlane android android_flutter_build +``` + +Compila la app con flutter + +### android android_upload + +```sh +[bundle exec] fastlane android android_upload +``` + +Sube el AAB a Google Play + +### android android_build_and_upload + +```sh +[bundle exec] fastlane android android_build_and_upload +``` + +Build completo y upload a Google Play + +---- + + ## iOS ### ios build diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 6156f7f..5c8a480 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -1,137 +1,139 @@ -def ensure_keychain() - delete_keychain(name: ENV["KEYCHAIN_NAME"]) if File.exist? File.expand_path("~/Library/Keychains/#{ENV["KEYCHAIN_NAME"]}") - create_keychain( - name: ENV["KEYCHAIN_NAME"], - password: ENV["KEYCHAIN_PASSWORD"], - unlock: true, - timeout: 0, - default_keychain: true, - add_to_search_list: true - ) - - # Importar certificados raíz de Apple al keychain custom - ["/tmp/AppleWWDRCA.cer", "/tmp/AppleRootCA.cer"].each do |cert| - if File.exist?(cert) - UI.message("🔐 Importando certificado raíz: #{cert}") - import_certificate( - certificate_path: cert, - keychain_name: ENV["KEYCHAIN_NAME"], - keychain_password: ENV["KEYCHAIN_PASSWORD"], - ) +platform :ios do + def ensure_keychain() + delete_keychain(name: ENV["KEYCHAIN_NAME"]) if File.exist? File.expand_path("~/Library/Keychains/#{ENV["KEYCHAIN_NAME"]}") + create_keychain( + name: ENV["KEYCHAIN_NAME"], + password: ENV["KEYCHAIN_PASSWORD"], + unlock: true, + timeout: 0, + default_keychain: true, + add_to_search_list: true + ) + + # Importar certificados raíz de Apple al keychain custom + ["/tmp/AppleWWDRCA.cer", "/tmp/AppleRootCA.cer"].each do |cert| + if File.exist?(cert) + UI.message("🔐 Importando certificado raíz: #{cert}") + import_certificate( + certificate_path: cert, + keychain_name: ENV["KEYCHAIN_NAME"], + keychain_password: ENV["KEYCHAIN_PASSWORD"], + ) + end end end -end - -desc "Sincroniza certificados y perfiles de aprovisionamiento" -lane :ios_sync_certificates do - ensure_keychain() - match( - app_identifier: [ENV["APP_IDENTIFIER"]], - type: "appstore", # Siempre appstore para subir a la appstore, aunque el Matchfile tenga otro tipo. - git_url: ENV["MATCH_REPO"], - readonly: ENV["IS_CI"] == "true", - keychain_name: ENV["KEYCHAIN_NAME"], - keychain_password: ENV["KEYCHAIN_PASSWORD"] - ) -end - -desc "Compila la app con flutter" -lane :ios_flutter_build do - scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" - target = scheme == "production" ? "lib/main_prod.dart" : "lib/main_dev.dart" - skip_clean = ENV["SKIP_CLEAN"] == "true" - skip_pods = ENV["SKIP_PODS"] == "true" - version = ENV["APP_VERSION"] - build_number = ENV["APP_BUILD_NUMBER"] - - unless skip_clean - sh("flutter", "clean") - sh("flutter", "pub", "get") - end - unless skip_pods - cocoapods(use_bundle_exec: false, podfile: "ios") + desc "Sincroniza certificados y perfiles de aprovisionamiento" + lane :sync_certificates do + ensure_keychain() + match( + app_identifier: [ENV["APP_IDENTIFIER"]], + type: "appstore", # Siempre appstore para subir a la appstore, aunque el Matchfile tenga otro tipo. + git_url: ENV["MATCH_REPO"], + readonly: ENV["IS_CI"] == "true", + keychain_name: ENV["KEYCHAIN_NAME"], + keychain_password: ENV["KEYCHAIN_PASSWORD"] + ) end + desc "Compila la app con flutter" + lane :flutter_build do + scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" + target = scheme == "production" ? "lib/main_prod.dart" : "lib/main_dev.dart" + skip_clean = ENV["SKIP_CLEAN"] == "true" + skip_pods = ENV["SKIP_PODS"] == "true" + version = ENV["APP_VERSION"] + build_number = ENV["APP_BUILD_NUMBER"] + + unless skip_clean + sh("flutter", "clean") + sh("flutter", "pub", "get") + end + + unless skip_pods + cocoapods(use_bundle_exec: false, podfile: "ios") + end - command = ["flutter", "build", "ios", "--release", "--no-codesign", "--flavor=#{scheme}", "--target=#{target}"] - command << "--build-name=#{version}" if version && !version.empty? - command << "--build-number=#{build_number}" if build_number && !build_number.empty? - sh(*command) -end + command = ["flutter", "build", "ios", "--release", "--no-codesign", "--flavor=#{scheme}", "--target=#{target}"] + command << "--build-name=#{version}" if version && !version.empty? + command << "--build-number=#{build_number}" if build_number && !build_number.empty? -desc "Compila la aplicación" -lane :ios_build do - skip_flutter_build = ENV["SKIP_FLUTTER_BUILD"] == "true" - skip_build_app = ENV["SKIP_BUILD_APP"] == "true" - - unless skip_flutter_build - ios_flutter_build() + sh(*command) end - scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" + desc "Compila la aplicación" + lane :build do + skip_flutter_build = ENV["SKIP_FLUTTER_BUILD"] == "true" + skip_build_app = ENV["SKIP_BUILD_APP"] == "true" + + unless skip_flutter_build + flutter_build() + end + + scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" + + sync_certificates() - ios_sync_certificates() + unless skip_build_app + update_code_signing_settings( + use_automatic_signing: false, + path: "./ios/Runner.xcodeproj", + team_id: ENV["TEAM_ID"], + bundle_identifier: ENV["APP_IDENTIFIER"], + profile_name: "match AppStore #{ENV["APP_IDENTIFIER"]}", + code_sign_identity: "Apple Distribution" + ) + + build_app( + scheme: scheme, + export_method: "app-store", # Siempre app-store para subir a la appstore. + workspace: "./ios/Runner.xcworkspace", + output_directory: "./build/ios/ipa", + output_name: "MiUTEM.ipa", + xcargs: "CODE_SIGN_STYLE=Manual PROVISIONING_PROFILE_SPECIFIER='match AppStore #{ENV["APP_IDENTIFIER"]}'" + ) + end + end - unless skip_build_app - update_code_signing_settings( - use_automatic_signing: false, - path: "./ios/Runner.xcodeproj", - team_id: ENV["TEAM_ID"], - bundle_identifier: ENV["APP_IDENTIFIER"], - profile_name: "match AppStore #{ENV["APP_IDENTIFIER"]}", - code_sign_identity: "Apple Distribution" + desc "Carga la API Key de App Store Connect para autenticación" + lane :load_api_key do + app_store_connect_api_key( + key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"], + issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"], + key_content: ENV["APP_STORE_CONNECT_API_KEY_CONTENT"], + is_key_content_base64: true, + in_house: false, # Se especifica false para App Store (nosotros), true para Enterprise ) + end + + desc "Sube la aplicación a TestFlight (requiere IPA)" + lane :upload do |options| + # Obtener path del IPA: lane_context si existe, sino argumento custom + ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH] || options[:ipa_path] + + # Validar que existe + UI.user_error!("❌ No se encontró IPA. Proporciona --ipa_path o ejecuta build primero") unless ipa_path + UI.user_error!("❌ El archivo IPA no existe en: #{ipa_path}") unless File.exist?(ipa_path) - build_app( - scheme: scheme, - export_method: "app-store", # Siempre app-store para subir a la appstore. - workspace: "./ios/Runner.xcworkspace", - output_directory: "./build/ios/ipa", - output_name: "MiUTEM.ipa", - xcargs: "CODE_SIGN_STYLE=Manual PROVISIONING_PROFILE_SPECIFIER='match AppStore #{ENV["APP_IDENTIFIER"]}'" + UI.success("✅ Usando IPA: #{ipa_path}") + + # Cargar API key + load_api_key() + + # Upload a TestFlight + upload_to_testflight( + ipa: ipa_path, + skip_submission: true, + skip_waiting_for_build_processing: true, ) + + UI.success("✅ IPA subido a TestFlight exitosamente") + end + + desc "Build completo y upload a TestFlight" + lane :deploy do + build() + upload() end -end - -desc "Carga la API Key de App Store Connect para autenticación" -lane :ios_load_api_key do - app_store_connect_api_key( - key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"], - issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"], - key_content: ENV["APP_STORE_CONNECT_API_KEY_CONTENT"], - is_key_content_base64: true, - in_house: false, # Se especifica false para App Store (nosotros), true para Enterprise - ) -end - -desc "Sube la aplicación a TestFlight (requiere IPA)" -lane :ios_upload do |options| - # Obtener path del IPA: lane_context si existe, sino argumento custom - ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH] || options[:ipa_path] - - # Validar que existe - UI.user_error!("❌ No se encontró IPA. Proporciona --ipa_path o ejecuta ios_build primero") unless ipa_path - UI.user_error!("❌ El archivo IPA no existe en: #{ipa_path}") unless File.exist?(ipa_path) - - UI.success("✅ Usando IPA: #{ipa_path}") - - # Cargar API key - ios_load_api_key() - - # Upload a TestFlight - upload_to_testflight( - ipa: ipa_path, - skip_submission: true, - skip_waiting_for_build_processing: true, - ) - - UI.success("✅ IPA subido a TestFlight exitosamente") -end - -desc "Build completo y upload a TestFlight" -lane :ios_build_and_upload do - ios_build() - ios_upload() end \ No newline at end of file From ee150a2b858c4f3510f4b097745b60c74dc44ed1 Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Sun, 24 May 2026 17:19:47 -0400 Subject: [PATCH 03/10] patch: se agrega configuracion de vscode --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7b016a8..fa6aa5b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "java.compile.nullAnalysis.mode": "automatic" + "java.compile.nullAnalysis.mode": "automatic", + "git.ignoredRepositories": [ + "/opt/homebrew/share/flutter" + ] } \ No newline at end of file From dce5b0e40cc39a9e6c4182261284f68ae8bb522d Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Sun, 24 May 2026 17:20:20 -0400 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20se=20agrega=20configuraci=C3=B3n?= =?UTF-8?q?=20de=20android=20para=20compilar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle.kts | 36 +++++++++-- android/build.gradle.kts | 1 + android/fastlane/Fastfile | 61 ++++++++++++++----- android/gradle.properties | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 06accab..e4fbb92 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.io.FileInputStream import java.util.Properties @@ -15,7 +14,7 @@ plugins { kotlin { compilerOptions { - jvmTarget = JvmTarget.fromTarget("17") + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget("17") } } @@ -35,10 +34,21 @@ android { keystoreProperties.load(FileInputStream(keystorePropertiesFile)) } - val resolvedStoreFile = System.getenv("MIUTEM_KEYSTORE_PATH") ?: keystoreProperties["storeFile"] as String? - val resolvedStorePassword = System.getenv("MIUTEM_KEYSTORE_PASSWORD") ?: keystoreProperties["storePassword"] as String? - val resolvedKeyAlias = System.getenv("MIUTEM_KEY_ALIAS") ?: keystoreProperties["keyAlias"] as String? - val resolvedKeyPassword = System.getenv("MIUTEM_KEY_PASSWORD") ?: keystoreProperties["keyPassword"] as String? + val resolvedStoreFile = System.getenv("MIUTEM_KEYSTORE_PATH") + ?: (project.findProperty("MIUTEM_KEYSTORE_PATH") as String?) + ?: (keystoreProperties["storeFile"] as String?) + val resolvedStorePassword = System.getenv("MIUTEM_KEYSTORE_PASSWORD") + ?: (project.findProperty("MIUTEM_KEYSTORE_PASSWORD") as String?) + ?: (keystoreProperties["storePassword"] as String?) + val resolvedKeyAlias = System.getenv("MIUTEM_KEY_ALIAS") + ?: (project.findProperty("MIUTEM_KEY_ALIAS") as String?) + ?: (keystoreProperties["keyAlias"] as String?) + val resolvedKeyPassword = System.getenv("MIUTEM_KEY_PASSWORD") + ?: (project.findProperty("MIUTEM_KEY_PASSWORD") as String?) + ?: (keystoreProperties["keyPassword"] as String?) + val useDebugSigning = System.getenv("MIUTEM_USE_DEBUG_SIGNING")?.toBoolean() ?: false + + val debugKeystore = file("${System.getProperty("user.home")}/.android/debug.keystore") signingConfigs { create("release") { @@ -47,6 +57,12 @@ android { storeFile = resolvedStoreFile?.let { file(it) } storePassword = resolvedStorePassword } + getByName("debug") { + keyAlias = if (useDebugSigning) "androiddebugkey" else (resolvedKeyAlias ?: "androiddebugkey") + keyPassword = if (useDebugSigning) "android" else (resolvedKeyPassword ?: "android") + storeFile = if (useDebugSigning) debugKeystore else (resolvedStoreFile?.let { file(it) } ?: debugKeystore) + storePassword = if (useDebugSigning) "android" else (resolvedStorePassword ?: "android") + } } defaultConfig { @@ -58,10 +74,18 @@ android { versionName = flutter.versionName } + buildFeatures { + resValues = true + } + buildTypes { named("release") { signingConfig = signingConfigs.getByName("release") } + + named("debug") { + signingConfig = signingConfigs.getByName("debug") + } } flavorDimensions += "default" diff --git a/android/build.gradle.kts b/android/build.gradle.kts index e265e2b..99bfdde 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -32,5 +32,6 @@ subprojects { } tasks.register("clean") { + description = "Delete rootProject buildDirectory" delete(rootProject.layout.buildDirectory) } \ No newline at end of file diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index 0294898..04cb97d 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -1,23 +1,56 @@ default_platform(:android) platform :android do - desc "Runs all the tests" - lane :test do - gradle(task: "test") + desc "Compila la app con flutter" + lane :build do + scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" + target = scheme == "production" ? "lib/main_prod.dart" : "lib/main_dev.dart" + skip_clean = ENV["SKIP_CLEAN"] == "true" + version = ENV["APP_VERSION"] + build_number = ENV["APP_BUILD_NUMBER"] + + unless skip_clean + sh("flutter", "clean") + sh("flutter", "pub", "get") + end + + command = [ + "flutter", "build", "appbundle", + "--release", + "--flavor=#{scheme}", + "--target=#{target}" + ] + command << "--build-name=#{version}" if version && !version.empty? + command << "--build-number=#{build_number}" if build_number && !build_number.empty? + + sh(*command) end - desc "Submit a new Beta Build to Crashlytics Beta" - lane :beta do - gradle(task: "clean assembleRelease") - crashlytics - - # sh "your_script.sh" - # You can also use other beta testing services here + desc "Sube el AAB a Google Play" + lane :upload do |options| + scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" + aab_path = options[:aab_path] || "../build/app/outputs/bundle/#{scheme}Release/app-#{scheme}-release.aab" + + UI.user_error!("❌ El archivo AAB no existe en: #{aab_path}") unless File.exist?(File.expand_path(aab_path, __dir__)) + UI.success("✅ Usando AAB: #{aab_path}") + + upload_to_play_store( + track: ENV["ANDROID_TRACK"] || "internal", + aab: aab_path, + release_status: "draft", + skip_upload_apk: true, + skip_upload_metadata: true, + skip_upload_changelogs: true, + skip_upload_images: true, + skip_upload_screenshots: true, + ) + + UI.success("✅ AAB subido a Google Play (#{ENV["ANDROID_TRACK"] || "internal"}) exitosamente") end - desc "Deploy a new version to the Google Play" + desc "Build completo y upload a Google Play" lane :deploy do - gradle(task: "clean assembleRelease") - upload_to_play_store + build() + upload() end -end +end \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index 31b7c0c..7eaeaca 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,6 @@ org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true -kotlin.jvm.target.validation.mode=IGNORE \ No newline at end of file +kotlin.jvm.target.validation.mode=IGNORE +android.newDsl=false +android.builtInKotlin=false \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 4d8652e..2f745f9 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-all.zip From 22110414229e4c925f39a4c8a8efd7d41d3b2506 Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Sun, 24 May 2026 17:20:28 -0400 Subject: [PATCH 05/10] patch: se actualiza dependencias --- pubspec.lock | 252 +++++++++++++++++++++++++++------------------------ 1 file changed, 134 insertions(+), 118 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index b164192..58bfee5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: cd83f7d6bd4e4c0b0b4fef802e8796784032e1cc23d7b0e982cf5d05d9bbe182 + sha256: "8f89e371e2883de35cdc78f648e725fa4da5f3b6c927269f00fa68f1ea92b598" url: "https://pub.dev" source: hosted - version: "1.3.66" + version: "1.3.71" adaptive_number: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "4.0.9" args: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.13.1" awesome_notifications: dependency: "direct main" description: @@ -125,34 +125,34 @@ packages: dependency: "direct main" description: name: cloud_firestore - sha256: "54484b2fc49f41b46f35b60a54b12351181eeaad22c0e3def276a81e17ae7c9b" + sha256: "6d965d2522a1cf6ed334098065a44e4e925686e83934927f4e3f8bce57f1a500" url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.4.1" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface - sha256: dfaa8b2c0d0a824af289d4159816a5c78417feec264c2194081d645687195158 + sha256: "17719eef8703c5f6b311d114d4bab278721f05523505c2a93b8332042042c263" url: "https://pub.dev" source: hosted - version: "7.0.6" + version: "8.0.1" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: "35d01f502b3b701d700470d32a8f82704dac8341a66e86c074900cde5bab343d" + sha256: "012e9e40a65fe49043f3050930c681f1f611b7c1c4e327715757dcf10d33c5f4" url: "https://pub.dev" source: hosted - version: "5.1.2" + version: "5.4.1" code_assets: dependency: transitive description: name: code_assets - sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" + sha256: dad6bf6b9f4f378b0a69edbf42584d336efd1a9ce15deb1ba591cbb1b5ff440f url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" collection: dependency: "direct main" description: @@ -197,10 +197,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.9" dart_jsonwebtoken: dependency: "direct main" description: @@ -213,26 +213,26 @@ packages: dependency: "direct main" description: name: dio - sha256: b9d46faecab38fc8cc286f80bc4d61a3bb5d4ac49e51ed877b4d6706efe57b25 + sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c url: "https://pub.dev" source: hosted - version: "5.9.1" + version: "5.9.2" dio_cache_interceptor: dependency: "direct main" description: name: dio_cache_interceptor - sha256: ac9f312e5a81d79cbccb15f56b78aeae7343a981c1d7c169b11194fae806ec0b + sha256: "3644ce3e0c9ba21885cbb0578b3d2ffe022c8badc6f6f4042cb56ecdbe90a7ad" url: "https://pub.dev" source: hosted - version: "4.0.5" + version: "4.0.6" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" dotted_border: dependency: "direct main" description: @@ -293,98 +293,98 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "8ffd91ae9f543c5ebbfec71a814ee5aa9e21176d31335133308abf63f4c42e8a" + sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" url: "https://pub.dev" source: hosted - version: "0.2.9" + version: "0.2.14" firebase_analytics: dependency: "direct main" description: name: firebase_analytics - sha256: "07e0a82e9b045dbd14522a0b23764e2dbcc269720e785d63c6a021133ebb766a" + sha256: "56641bc6dd7b6a8c63ad5f5d46664af5b66db452f1c5ad052b1eec0188cf3183" url: "https://pub.dev" source: hosted - version: "12.1.2" + version: "12.4.1" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "62fd3f27f342c898bd819fb97fa87c0b971e9fbe03357477282c0e14e1a40c3c" + sha256: "432492ba57024a35dfb67ebcf0c64bac31e19d2c46b3dd222bccbc05f8839efe" url: "https://pub.dev" source: hosted - version: "5.0.6" + version: "6.0.1" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "8fc488bb008439fc3b850cfac892dec1ff4cd438eee44438919a14c5e61b9828" + sha256: e613fca6511ab8f13adb355f7bc486c49fa6aea72fb6703cfb34df29b3b568a6 url: "https://pub.dev" source: hosted - version: "0.6.1+2" + version: "0.6.1+7" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "923085c881663ef685269b013e241b428e1fb03cdd0ebde265d9b40ff18abf80" + sha256: "93a5bde9775fd5adcc937f39dfa04ae0bc89c4d79bea6abc49de3f7b049d9ff6" url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.9.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 + sha256: "4a120366dbf7d5a8ee9438978530b664b855728fb8dcc3a201017660817e555b" url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "7.0.1" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "83e7356c704131ca4d8d8dd57e360d8acecbca38b1a3705c7ae46cc34c708084" + sha256: "7c98f10b8c8e5adedc0b810b66a877120696675e2c22d9ca9caca092da0d9e57" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.7.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: a6e6cb8b2ea1214533a54e4c1b11b19c40f6a29333f3ab0854a479fdc3237c5b + sha256: "9935ea36aaae0dfc789b80a1981fd70c6c7e9956791e9e7c2210ab83f9c963bd" url: "https://pub.dev" source: hosted - version: "5.0.7" + version: "5.2.2" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: fc6837c4c64c48fa94cab8a872a632b9194fa9208ca76a822f424b3da945584d + sha256: ade20d7d86698ded596bf16ab0e7060ef1dae9258ab6705536a39ae047ccef59 url: "https://pub.dev" source: hosted - version: "3.8.17" + version: "3.8.22" firebase_remote_config: dependency: "direct main" description: name: firebase_remote_config - sha256: "6d7be334b726537b3aa3a3ef1db9782951cd93c2b996eebc33f64a9f140e0409" + sha256: "5710137e7270ec92236e6ae4d96d36b5c59f5f25ac111d6f77c32e42d4cc01a2" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "6.5.1" firebase_remote_config_platform_interface: dependency: transitive description: name: firebase_remote_config_platform_interface - sha256: f895915923bebb9e8b36a6387fac31a9dd36fbb63fa2ee1ea2cbe309634eecdf + sha256: "509c6422f38e7c546b96005f0fbb4867c5c28b382717104c6b716fdb275062e1" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "3.0.1" firebase_remote_config_web: dependency: transitive description: name: firebase_remote_config_web - sha256: a4de6b86e5eb0416a46948dcb1fa9fce6a71d41f80fa107f6e202ae579ec81b2 + sha256: "37e3ec0667610e62cac1dd0bdd01d891654e3b196cd8eef45136eac317db3001" url: "https://pub.dev" source: hosted - version: "1.10.3" + version: "1.10.8" fixnum: dependency: transitive description: @@ -490,10 +490,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9" + sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.3.0" flutter_test: dependency: "direct dev" description: flutter @@ -520,14 +520,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" google_fonts: dependency: "direct main" description: @@ -540,10 +532,10 @@ packages: dependency: transitive description: name: hooks - sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6" + sha256: a41af4e8fc687cd6d33de9751eb936c8c0204ebe2bcb6c15ecf707504bf47f31 url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" html: dependency: "direct main" description: @@ -588,10 +580,10 @@ packages: dependency: transitive description: name: image - sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c" + sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce url: "https://pub.dev" source: hosted - version: "4.7.2" + version: "4.8.0" intl: dependency: transitive description: @@ -600,6 +592,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.20.2" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.dev" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.dev" + source: hosted + version: "1.0.1" js: dependency: transitive description: @@ -644,10 +652,10 @@ packages: dependency: "direct main" description: name: logger - sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 + sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c" url: "https://pub.dev" source: hosted - version: "2.6.2" + version: "2.7.0" logging: dependency: transitive description: @@ -676,18 +684,18 @@ packages: dependency: "direct main" description: name: material_symbols_icons - sha256: c62b15f2b3de98d72cbff0148812f5ef5159f05e61fc9f9a089ec2bb234df082 + sha256: "10a74aaa9e566c92f8aa14809d2dd78156fb93743348ebffec0345c38eb35706" url: "https://pub.dev" source: hosted - version: "4.2906.0" + version: "4.2928.1" meta: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -696,22 +704,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - native_toolchain_c: + objective_c: dependency: transitive description: - name: native_toolchain_c - sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac" + name: objective_c + sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" url: "https://pub.dev" source: hosted - version: "0.17.4" - objective_c: + version: "9.4.1" + package_config: dependency: transitive description: - name: objective_c - sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "9.3.0" + version: "2.2.0" package_info_plus: dependency: "direct main" description: @@ -764,10 +772,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" url: "https://pub.dev" source: hosted - version: "2.2.22" + version: "2.3.1" path_provider_foundation: dependency: transitive description: @@ -804,10 +812,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.2" platform: dependency: transitive description: @@ -836,10 +844,10 @@ packages: dependency: transitive description: name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "6.5.0" pub_semver: dependency: transitive description: @@ -872,6 +880,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.2" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.dev" + source: hosted + version: "0.6.0" rxdart: dependency: transitive description: @@ -932,18 +948,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.5" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 url: "https://pub.dev" source: hosted - version: "2.4.20" + version: "2.4.23" shared_preferences_foundation: dependency: transitive description: @@ -964,10 +980,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" shared_preferences_web: dependency: transitive description: @@ -988,10 +1004,10 @@ packages: dependency: "direct main" description: name: skeletonizer - sha256: "83157d8e2e41f0252079cfec496281c16e4c63660052dab8d4cd72a206bb7109" + sha256: "9f38f9b47ec3cf2235a6a4f154a88a95432bc55ba98b3e2eb6ced5c1974bc122" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" sky_engine: dependency: transitive description: flutter @@ -1009,26 +1025,26 @@ packages: dependency: "direct main" description: name: sqflite - sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 + sha256: "564cfed0746fe53140c23b70b308e045c3b31f17778f2f326ccb7d804ea0250a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.2+1" sqflite_android: dependency: transitive description: name: sqflite_android - sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 + sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" url: "https://pub.dev" source: hosted - version: "2.4.2+2" + version: "2.4.2+3" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" + sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465" url: "https://pub.dev" source: hosted - version: "2.5.6" + version: "2.5.8" sqflite_darwin: dependency: transitive description: @@ -1073,10 +1089,10 @@ packages: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "63896c27e81b28f8cb4e69ead0d3e8f03f1d1e5fc531a3e579cabed6a2c7c9e5" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.4.0+1" term_glyph: dependency: transitive description: @@ -1089,10 +1105,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" typed_data: dependency: transitive description: @@ -1121,18 +1137,18 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611" + sha256: "17bc677f0b301615530dd1d67e0a9828cafa2d0b6b6eae4cd3679b7eac4a273c" url: "https://pub.dev" source: hosted - version: "6.3.28" + version: "6.3.30" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: b1aca26728b7cc7a3af971bb6f601554a8ae9df2e0a006de8450ba06a17ad36a + sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" url: "https://pub.dev" source: hosted - version: "6.4.0" + version: "6.4.1" url_launcher_linux: dependency: transitive description: @@ -1161,10 +1177,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.3" url_launcher_windows: dependency: transitive description: @@ -1185,10 +1201,10 @@ packages: dependency: transitive description: name: vector_graphics - sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.2.2" vector_graphics_codec: dependency: transitive description: @@ -1201,10 +1217,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74" + sha256: b9b3f391857781aa96acacef96066f2f49b4cd03cf9fce3ca4d8da2ef5ea129e url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.3" vector_math: dependency: "direct main" description: @@ -1217,34 +1233,34 @@ packages: dependency: "direct main" description: name: video_player - sha256: "096bc28ce10d131be80dfb00c223024eb0fba301315a406728ab43dd99c45bdf" + sha256: "48a7bdaa38a3d50ec10c78627abdbfad863fdf6f0d6e08c7c3c040cfd80ae36f" url: "https://pub.dev" source: hosted - version: "2.10.1" + version: "2.11.1" video_player_android: dependency: transitive description: name: video_player_android - sha256: ee4fd520b0cafa02e4a867a0f882092e727cdaa1a2d24762171e787f8a502b0a + sha256: "877a6c7ba772456077d7bfd71314629b3fe2b73733ce503fc77c3314d43a0ca0" url: "https://pub.dev" source: hosted - version: "2.9.1" + version: "2.9.5" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - sha256: "0d753e1d6bcc741a9c182a689434a9f22041f8a2cf5b3bd3b46ae51f057947a3" + sha256: "9338f3ec22774f88146b22f13273a446719b1da010fd200c4d1d97802156ac58" url: "https://pub.dev" source: hosted - version: "2.9.2" + version: "2.9.7" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface - sha256: "57c5d73173f76d801129d0531c2774052c5a7c11ccb962f1830630decd9f24ec" + sha256: "16eaed5268c571c31840dc58ef8da5f0cd4db2a98490c3b8f1cf70122546c6e0" url: "https://pub.dev" source: hosted - version: "6.6.0" + version: "6.7.0" video_player_web: dependency: transitive description: @@ -1257,10 +1273,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" url: "https://pub.dev" source: hosted - version: "15.0.2" + version: "15.2.0" web: dependency: transitive description: From 8c5955e1cfb69252479b96b7a970840e241d2792 Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Sun, 24 May 2026 23:34:46 -0400 Subject: [PATCH 06/10] feat: se implementa setup del keystore --- android/app/build.gradle.kts | 2 +- android/fastlane/Fastfile | 26 ++++++++++++- android/gradle.properties | 3 +- fastlane/.env.example | 4 ++ fastlane/README.md | 71 +++++++++++++----------------------- pubspec.yaml | 2 +- 6 files changed, 58 insertions(+), 50 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index e4fbb92..2a0e189 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -21,7 +21,7 @@ kotlin { android { namespace = "cl.inndev.miutem" compileSdk = flutter.compileSdkVersion - ndkVersion = "29.0.14206865" + ndkVersion = project.findProperty("android.ndkVersion") as String? ?: "29.0.14206865" compileOptions { sourceCompatibility = JavaVersion.VERSION_17 diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index 04cb97d..4f7e68e 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -1,8 +1,27 @@ default_platform(:android) platform :android do + desc "Decodifica el keystore desde base64 y lo deja en un archivo temporal" + lane :setup_keystore do + keystore_base64 = ENV["MIUTEM_KEYSTORE_BASE64"] + + unless keystore_base64 && !keystore_base64.empty? + UI.message("⚠️ MIUTEM_KEYSTORE_BASE64 no definido, usando archivo local (si existe)") + next + end + + keystore_path = "/tmp/miutem.jks" + File.write(keystore_path, Base64.decode64(keystore_base64), mode: "wb") + ENV["MIUTEM_KEYSTORE_PATH"] = keystore_path + + UI.success("✅ Keystore decodificado en #{keystore_path}") + end + desc "Compila la app con flutter" lane :build do + # Asegura que el keystore esté configurado antes de compilar + setup_keystore() + scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" target = scheme == "production" ? "lib/main_prod.dart" : "lib/main_dev.dart" skip_clean = ENV["SKIP_CLEAN"] == "true" @@ -17,6 +36,7 @@ platform :android do command = [ "flutter", "build", "appbundle", "--release", + "--no-tree-shake-icons", "--flavor=#{scheme}", "--target=#{target}" ] @@ -31,18 +51,20 @@ platform :android do scheme = ENV["APP_ENV"] == "prod" ? "production" : "development" aab_path = options[:aab_path] || "../build/app/outputs/bundle/#{scheme}Release/app-#{scheme}-release.aab" - UI.user_error!("❌ El archivo AAB no existe en: #{aab_path}") unless File.exist?(File.expand_path(aab_path, __dir__)) + UI.user_error!("❌ El archivo AAB no existe en: #{aab_path}") unless File.exist?(aab_path) UI.success("✅ Usando AAB: #{aab_path}") upload_to_play_store( track: ENV["ANDROID_TRACK"] || "internal", - aab: aab_path, + aab: File.expand_path(aab_path), release_status: "draft", skip_upload_apk: true, skip_upload_metadata: true, skip_upload_changelogs: true, skip_upload_images: true, skip_upload_screenshots: true, + json_key_data: ENV["GOOGLE_PLAY_JSON_KEY"], + package_name: ENV["APP_PACKAGE_NAME"], ) UI.success("✅ AAB subido a Google Play (#{ENV["ANDROID_TRACK"] || "internal"}) exitosamente") diff --git a/android/gradle.properties b/android/gradle.properties index 7eaeaca..b9c021f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -3,4 +3,5 @@ android.useAndroidX=true android.enableJetifier=true kotlin.jvm.target.validation.mode=IGNORE android.newDsl=false -android.builtInKotlin=false \ No newline at end of file +android.builtInKotlin=false +android.ndkVersion=29.0.14206865 \ No newline at end of file diff --git a/fastlane/.env.example b/fastlane/.env.example index ac7d4cf..e2562df 100644 --- a/fastlane/.env.example +++ b/fastlane/.env.example @@ -37,6 +37,10 @@ APP_STORE_CONNECT_API_KEY_CONTENT= # Android keystore configuration MIUTEM_KEYSTORE_PATH= + +# Si estamos en CI/CD usamos la variable MIUTEM_KEYSTORE_BASE64 para cargar el keystore desde una variable de entorno, en lugar de usar un archivo local. +# MIUTEM_KEYSTORE_BASE64= + MIUTEM_KEYSTORE_PASSWORD= MIUTEM_KEY_ALIAS= MIUTEM_KEY_PASSWORD= diff --git a/fastlane/README.md b/fastlane/README.md index f958bf8..2b7324f 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -13,50 +13,52 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do # Available Actions -### ios_sync_certificates +## iOS + +### ios sync_certificates ```sh -[bundle exec] fastlane ios_sync_certificates +[bundle exec] fastlane ios sync_certificates ``` Sincroniza certificados y perfiles de aprovisionamiento -### ios_flutter_build +### ios flutter_build ```sh -[bundle exec] fastlane ios_flutter_build +[bundle exec] fastlane ios flutter_build ``` Compila la app con flutter -### ios_build +### ios build ```sh -[bundle exec] fastlane ios_build +[bundle exec] fastlane ios build ``` Compila la aplicación -### ios_load_api_key +### ios load_api_key ```sh -[bundle exec] fastlane ios_load_api_key +[bundle exec] fastlane ios load_api_key ``` Carga la API Key de App Store Connect para autenticación -### ios_upload +### ios upload ```sh -[bundle exec] fastlane ios_upload +[bundle exec] fastlane ios upload ``` Sube la aplicación a TestFlight (requiere IPA) -### ios_build_and_upload +### ios deploy ```sh -[bundle exec] fastlane ios_build_and_upload +[bundle exec] fastlane ios deploy ``` Build completo y upload a TestFlight @@ -66,58 +68,37 @@ Build completo y upload a TestFlight ## Android -### android android_flutter_build +### android setup_keystore ```sh -[bundle exec] fastlane android android_flutter_build +[bundle exec] fastlane android setup_keystore ``` -Compila la app con flutter +Decodifica el keystore desde base64 y lo deja en un archivo temporal -### android android_upload +### android build ```sh -[bundle exec] fastlane android android_upload +[bundle exec] fastlane android build ``` -Sube el AAB a Google Play - -### android android_build_and_upload - -```sh -[bundle exec] fastlane android android_build_and_upload -``` - -Build completo y upload a Google Play - ----- - - -## iOS - -### ios build - -```sh -[bundle exec] fastlane ios build -``` - -Compila la aplicación para iOS +Compila la app con flutter -### ios upload +### android upload ```sh -[bundle exec] fastlane ios upload +[bundle exec] fastlane android upload ``` -Sube la aplicación a TestFlight (requiere IPA) +Sube el AAB a Google Play -### ios build_and_upload +### android deploy ```sh -[bundle exec] fastlane ios build_and_upload +[bundle exec] fastlane android deploy ``` -Build completo y upload a TestFlight +Build completo y upload a Google Play ---- diff --git a/pubspec.yaml b/pubspec.yaml index 967f514..942dd6a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: miutem description: "Plataforma académica para estudiantes de la Universidad Tecnológica Metropolitana (UTEM)" publish_to: none -version: 4.0.0+9 +version: 4.0.0+133 environment: sdk: ^3.11.1 From 6f4815ee4fdd86378754def8a6d05b2d6d3c632e Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Sun, 24 May 2026 23:37:01 -0400 Subject: [PATCH 07/10] patch: bump version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 942dd6a..c660768 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: miutem description: "Plataforma académica para estudiantes de la Universidad Tecnológica Metropolitana (UTEM)" publish_to: none -version: 4.0.0+133 +version: 4.0.0+134 environment: sdk: ^3.11.1 From 94a4378deb38890c9c61a3417de49fa68d54a144 Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Mon, 25 May 2026 00:23:20 -0400 Subject: [PATCH 08/10] patch: excluye build del analisis --- analysis_options.yaml | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 4a6db94..d0da92c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,12 +1,3 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml analyzer: @@ -14,18 +5,9 @@ analyzer: - lib/firebase_options_*.dart - lib/main_dev.dart - lib/main_prod.dart + - build/** linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: # Reglas habilitadas - sized_box_for_whitespace From a7135b3b766ed37c6bf8f8ce23d0eed90cc8c515 Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Mon, 25 May 2026 00:23:33 -0400 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20se=20mejora=20automatizaci=C3=B3n?= =?UTF-8?q?=20de=20despliegue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 98 +++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5ccfb86..11715fb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,7 +1,7 @@ name: Despliegue de la aplicación on: push: - branches: [dev] + branches: [dev, prod] workflow_dispatch: inputs: track: @@ -12,16 +12,78 @@ on: options: - production - development -env: - FLUTTER_VERSION: "3.41.6" - FLUTTER_CHANNEL: "stable" - XCODE_VERSION: "26" - RUBY_VERSION: "4.0.1" - jobs: - deploy-ios-dev: + determine-environment: + runs-on: ubuntu-latest + outputs: + environment: ${{ steps.set-environment.outputs.environment }} + steps: + - name: Determinar entorno de despliegue + id: set-environment + run: | + if [[ "${{ inputs.track }}" == "production" || "${{ github.ref }}" == "refs/heads/prod" ]]; then + echo "environment=production" >> $GITHUB_OUTPUT + else + echo "environment=development" >> $GITHUB_OUTPUT + fi + + deploy-android: + needs: determine-environment + # Por ahora vamos a limitar el despliegue de android a producción, ya que aún debemos crear la app de desarrollo para el track de desarrollo. + if: ${{ needs.determine-environment.outputs.environment == 'production' }} + runs-on: ubuntu-latest + environment: ${{ needs.determine-environment.outputs.environment }} + timeout-minutes: 60 + steps: + - name: 📚 Clonar Repositorio + uses: actions/checkout@v4 + + - name: ⚡ Configurar Cache para Gradle + uses: actions/cache/restore@v5 + with: + path: ~/.gradle/caches + key: gradle-caches-${{ needs.determine-environment.outputs.environment }}-${{ github.run_id }} + restore-keys: | + gradle-caches-${{ needs.determine-environment.outputs.environment }}- + + - name: 🐦 Configurar Flutter SDK + uses: flutter-actions/setup-flutter@v4 + with: + cache: true + channel: ${{ vars.FLUTTER_CHANNEL }} + version: ${{ vars.FLUTTER_VERSION }} + + - name: 🗳️ Instalar firebase-tools + run: npm install -g firebase-tools + + - name: 📦 Instalar librerías + run: flutter pub get + + - name: 🔥 Instalar FlutterFire + run: dart pub global activate flutterfire_cli + + - name: 🔐 Configura credenciales de firebase + env: + FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + run: ./scripts/flutterfire-configure prod + + - name: 🚀 Desplegar a Play Store + run: bundle exec fastlane android deploy + env: + IS_CI: ${{ vars.IS_CI }} + APP_PACKAGE_NAME: ${{ secrets.APP_PACKAGE_NAME }} + APP_ENV: ${{ vars.APP_ENV }} + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} + GITHUB_TOKEN: ${{ github.token }} + MIUTEM_KEYSTORE_BASE64: ${{ secrets.MIUTEM_KEYSTORE_BASE64 }} + MIUTEM_KEYSTORE_PASSWORD: ${{ secrets.MIUTEM_KEYSTORE_PASSWORD }} + MIUTEM_KEY_ALIAS: ${{ secrets.MIUTEM_KEY_ALIAS }} + MIUTEM_KEY_PASSWORD: ${{ secrets.MIUTEM_KEY_PASSWORD }} + GOOGLE_PLAY_JSON_KEY: ${{ secrets.GOOGLE_PLAY_JSON_KEY }} + deploy-ios: + needs: determine-environment runs-on: macos-latest - environment: development + environment: ${{ needs.determine-environment.outputs.environment }} timeout-minutes: 60 steps: - name: 📚 Clonar Repositorio @@ -30,7 +92,7 @@ jobs: - name: ⚒️ Configurar Xcode uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: ${{ env.XCODE_VERSION }} + xcode-version: ${{ vars.XCODE_VERSION }} - name: 🔏 Descargar certificados raíz de Apple run: | @@ -41,24 +103,23 @@ jobs: - name: ⚡ Configurar Cache para DerivedData uses: actions/cache/restore@v5 - id: cache-derived-data with: path: DerivedData - key: derived-data-${{ github.run_id }} + key: derived-data-${{ needs.determine-environment.outputs.environment }}-${{ github.run_id }} restore-keys: | - derived-data- + derived-data-${{ needs.determine-environment.outputs.environment }}- - name: 🐦 Configurar Flutter SDK uses: flutter-actions/setup-flutter@v4 with: cache: true - channel: ${{ env.FLUTTER_CHANNEL }} - version: ${{ env.FLUTTER_VERSION }} + channel: ${{ vars.FLUTTER_CHANNEL }} + version: ${{ vars.FLUTTER_VERSION }} - name: 💎 Configurar Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ env.RUBY_VERSION }} + ruby-version: ${{ vars.RUBY_VERSION }} bundler-cache: true - name: 🗳️ Instalar firebase-tools @@ -76,17 +137,16 @@ jobs: - name: 🪎 Configura bundle run: bundle install - - name: 🔐 Configura credenciales de firebase + - name: 🔐 Configura credenciales de firebase (${{ needs.determine-environment.outputs.environment }}) env: FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} - run: ./scripts/flutterfire-configure dev + run: ./scripts/flutterfire-configure ${{ needs.determine-environment.outputs.environment == 'production' && 'prod' || 'dev' }} - name: 🚀 Desplegar a TestFlight run: bundle exec fastlane ios deploy env: IS_CI: ${{ vars.IS_CI }} APP_IDENTIFIER: ${{ secrets.APP_IDENTIFIER }} - APP_PACKAGE_NAME: ${{ secrets.APP_PACKAGE_NAME }} APP_ENV: ${{ vars.APP_ENV }} APPLE_ID: ${{ secrets.APPLE_ID }} ITC_TEAM_ID: ${{ secrets.ITC_TEAM_ID }} From e9980595329fab10081588ac203a45220ba3cada Mon Sep 17 00:00:00 2001 From: Francisco Solis <30329003+Im-Fran@users.noreply.github.com> Date: Mon, 25 May 2026 00:25:52 -0400 Subject: [PATCH 10/10] feat: se mejora workflows de lint y pruebas --- .github/workflows/lint.yml | 13 +++++++------ .github/workflows/pruebas.yml | 15 ++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 77ef468..81a6e43 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,16 +7,17 @@ on: jobs: analizar: runs-on: macos-latest + environment: development steps: - - name: Clonar Repositorio + - name: 📚 Clonar Repositorio uses: actions/checkout@v4 - - name: Configurar Flutter SDK + - name: 🐦 Configurar Flutter SDK uses: flutter-actions/setup-flutter@v4 with: cache: true - channel: stable - version: latest - - name: Instalar librerías + channel: ${{ vars.FLUTTER_CHANNEL }} + version: ${{ vars.FLUTTER_VERSION }} + - name: 📦 Instalar librerías run: flutter pub get - - name: Analizar código + - name: 🕵️ Analizar código run: flutter analyze --fatal-infos --fatal-warnings diff --git a/.github/workflows/pruebas.yml b/.github/workflows/pruebas.yml index 9854de2..d4feb86 100644 --- a/.github/workflows/pruebas.yml +++ b/.github/workflows/pruebas.yml @@ -8,16 +8,17 @@ on: jobs: probar: runs-on: macos-latest + environment: development steps: - - name: Clonar Repositorio + - name: 📚 Clonar Repositorio uses: actions/checkout@v4 - - name: Configurar Flutter SDK + - name: 🐦 Configurar Flutter SDK uses: flutter-actions/setup-flutter@v4 with: cache: true - channel: stable - version: latest - - name: Instalar librerías + channel: ${{ vars.FLUTTER_CHANNEL }} + version: ${{ vars.FLUTTER_VERSION }} + - name: 📦 Instalar librerías run: flutter pub get - - name: Ejecutar pruebas unitarias - run: flutter test --coverage --test-randomize-ordering-seed=random + - name: 🧪 Ejecutar pruebas unitarias + run: flutter test --coverage --test-randomize-ordering-seed=${{ github.run_id }}