diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9e651bb..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 build_and_upload + 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 }} 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 }} 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 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/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 diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index d63d899..2a0e189 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,14 +14,14 @@ plugins { kotlin { compilerOptions { - jvmTarget = JvmTarget.fromTarget("17") + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget("17") } } 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 @@ -35,12 +34,34 @@ android { keystoreProperties.load(FileInputStream(keystorePropertiesFile)) } + 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") { - 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 + } + 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") } } @@ -53,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/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/android/fastlane/Fastfile b/android/fastlane/Fastfile index 0294898..4f7e68e 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -1,23 +1,78 @@ default_platform(:android) platform :android do - desc "Runs all the tests" - lane :test do - gradle(task: "test") + 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 "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 "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" + 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", + "--no-tree-shake-icons", + "--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 "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?(aab_path) + UI.success("✅ Usando AAB: #{aab_path}") + + upload_to_play_store( + track: ENV["ANDROID_TRACK"] || "internal", + 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") 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..b9c021f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,7 @@ 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 +android.ndkVersion=29.0.14206865 \ 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 diff --git a/fastlane/.env.example b/fastlane/.env.example index d080f41..e2562df 100644 --- a/fastlane/.env.example +++ b/fastlane/.env.example @@ -33,4 +33,19 @@ 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= + +# 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= +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..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 @@ -64,31 +66,39 @@ Build completo y upload a TestFlight ---- -## iOS +## Android -### ios build +### android setup_keystore ```sh -[bundle exec] fastlane ios build +[bundle exec] fastlane android setup_keystore ``` -Compila la aplicación para iOS +Decodifica el keystore desde base64 y lo deja en un archivo temporal -### ios upload +### android build ```sh -[bundle exec] fastlane ios upload +[bundle exec] fastlane android build ``` -Sube la aplicación a TestFlight (requiere IPA) +Compila la app con flutter -### ios build_and_upload +### android upload ```sh -[bundle exec] fastlane ios build_and_upload +[bundle exec] fastlane android upload ``` -Build completo y upload a TestFlight +Sube el AAB a Google Play + +### android deploy + +```sh +[bundle exec] fastlane android deploy +``` + +Build completo y upload a Google Play ---- 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 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: diff --git a/pubspec.yaml b/pubspec.yaml index 967f514..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+9 +version: 4.0.0+134 environment: sdk: ^3.11.1 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