Estrategia de actualización y distribución: App Store, Play Store, Tauri y Electron
Dividimos las actualizaciones de aplicaciones realizadas con código web en rutas de distribución App Store·Google Play·Tauri·Electron y resumimos cómo diseñar firmas, versiones, canales de lanzamiento y reversiones.
Las actualizaciones no son el último trabajo del equipo de implementación
En un servicio web, el momento en que lo carga en el servidor parece ser el final de la implementación. Las aplicaciones son diferentes. El dispositivo del usuario ya contiene ejecutables firmados, datos anteriores, permisos del sistema operativo y versiones administradas por la tienda. Por lo tanto, las actualizaciones tienen menos que ver con “agregar nuevas funciones” y más con acercar a los usuarios de diferentes versiones de manera segura al mismo producto.
Es especialmente fácil confundirse cuando se trata de aplicaciones creadas con código web. El hecho de que una pantalla esté hecha de HTML y JavaScript no significa que puedas cambiar el código de la pantalla en cualquier momento. Las actualizaciones automáticas para la tienda iOS·Android, el instalador de escritorio y Tauri·Electron funcionan según diferentes mecanismos de confianza y expectativas del usuario.
Este artículo no compara cuál es más rápido: Capacitor·Tauri·Electron. Creación de aplicaciones con código web: Tauri·Electron·Capacitor Guía de selección describe cómo mover el tiempo de ejecución seleccionado a operaciones de lanzamiento reales. Si necesita una comparación de implementación Tauri vs Electron: SvelteKit criterios de selección de aplicaciones de escritorio, si tiene curiosidad sobre el límite entre las extensiones móviles y de escritorio, [Capacitor vs Tauri: One web También puede leer Criterios de selección para extender la Base de código para aplicaciones móviles y de escritorio.
Primero, separe los objetivos de actualización en tres tipos.
Enviar todos los cambios con la misma urgencia dificulta las operaciones. Es una buena idea separar los cambios en tres categorías antes del primer lanzamiento.
| cambiar objetivo | ejemplo | Ruta de implementación predeterminada | Un récord que hay que dejar atrás |
|---|---|---|---|
| Datos/contenido del servidor | Texto de anuncio, lista recomendada, valores de configuración remota | Implementación del servidor | modificador, tiempo, valor anterior |
| Activos web y lógica de producto. | Comportamiento de pantalla, gestión de estado, método de llamada API | Nueva versión del instalador/binario de la aplicación | Versión de la aplicación, compilación SHA, rango de compatibilidad |
| Funciones/permisos nativos | Cámara, Push, Sistema de Archivos, Pago SDK | Versión almacenada o de escritorio firmada | Clave de firma, derechos/permisos, resultado de la verificación |
Esta distinción no se trata sólo de "qué actualizaciones se revisan". La forma de recuperarse cuando se produce un fallo también cambia. Si la configuración remota es incorrecta, puede desactivarla en el servidor, pero es posible que la versión de la aplicación que cambió el formato de datos local ya esté en el dispositivo. Por lo tanto, la migración de datos debe tener un plan de reversión antes de que se marquen las funciones.
Sería una buena idea que el equipo de producto anotara las siguientes cuatro líneas en cada ticket:
change:
user_value: "오프라인에서도 최근 작성물을 다시 연다"
touches_native_boundary: true
minimum_app_version: "1.4.0"
rollback: "읽기 전용 모드로 전환하고 1.3 데이터 포맷을 유지"
El estado "distribuible" no significa que el código haya sido elaborado, sino que la respuesta está en estas cuatro líneas.
App Store analizan juntos el contenido de la aplicación y la ruta de actualización
Si distribuye a través de iOS y macOS App Store, su aplicación seguirá la ruta de actualización administrada por la tienda. En particular, existe una estipulación de que las aplicaciones macOS App Store no deben utilizar ningún otro mecanismo de actualización. Aunque pueda parecer una simple actualización de la aplicación para los usuarios, desde la perspectiva del equipo, los envíos a la tienda, las funciones revisables y los metadatos de la versión son una unidad.
Lo que a menudo se pasa por alto acerca de la aplicación Capacitor es que no es una “aplicación envuelta alrededor de una pantalla web”. App Review comprueba si una aplicación proporciona una utilidad continua y una experiencia similar a la de una aplicación más allá de simplemente mostrar un sitio web. Cuanto más exista un flujo de uso real que combine cámaras, notificaciones, acciones fuera de línea, uso compartido y estado del dispositivo, más claro será el por qué del producto. Por el contrario, los inicios de sesión temporales, las pantallas en blanco y las condiciones del servidor que los revisores no pueden reproducir reducen las posibilidades de aprobar, incluso con buenas actualizaciones.
iOS Preguntas para la publicación
- ¿Pueden los revisores comprobar las funciones modificadas en esta versión sin iniciar sesión o utilizar una cuenta de prueba proporcionada?
- ¿Se revela suficientemente el contexto de la función en el momento en que se solicita el permiso por primera vez?
- ¿La pantalla cambiada en la web realmente se incluyó en el binario de envío de la tienda?
- Incluso si se trata de una solución de emergencia, si el código ejecutable o el comportamiento nativo cambian, ¿debería manejarse como la próxima versión de la tienda?
- ¿El servidor API admite versiones anteriores de la aplicación durante un período de tiempo determinado incluso si el usuario no actualiza?
Esta última pregunta a menudo se pasa por alto. El hecho de que envíes una nueva versión de tu aplicación no significa que todos los usuarios la actualizarán el mismo día. Al cambiar los campos obligatorios en API o interrumpir el flujo de autenticación, debe implementar en función de la aplicación que admitirá por más tiempo, no de la aplicación más nueva.
// 서버가 앱 버전을 읽고 점진적으로 응답을 바꾸는 단순한 예시
type ClientInfo = { platform: 'ios' | 'android' | 'desktop' | 'web'; version: string };
function supportsSavedSearchV2(client: ClientInfo) {
return client.platform !== 'ios' || compareSemver(client.version, '1.4.0') >= 0;
}
function serializeSavedSearch(client: ClientInfo, search: SavedSearch) {
return supportsSavedSearchV2(client)
? { id: search.id, query: search.query, filters: search.filters }
: { id: search.id, query: search.query };
}
Este código no es una herramienta de control de versiones completa. Sin embargo, revela a nivel de código si los cambios en el servidor son "posibles sólo después de una actualización de la aplicación". Aunque la versión del cliente no debe usarse para tomar decisiones de confianza y seguridad, es útil como señal para seleccionar tipos de respuesta compatibles.
Google Play es una versión seguida del código de versión y la firma.
En Android, se deben cumplir condiciones como ID de aplicación, certificado de firma y código de versión para que se instale una actualización en una aplicación existente. Específicamente, Google Play determina que su aplicación es más nueva según el código de la versión superior. El mero hecho de que “se haya creado un nuevo archivo APK” no constituye una actualización.
Al administrar el proyecto Android de Capacitor, debe comprender los códigos de la versión de compilación web y de la versión Android por separado. Los cambios en los recursos de la pantalla comienzan en la compilación web, pero lo que va a la tienda es el paquete de aplicaciones Android. Mientras tanto, debe dejar un registro de la entrada cap sync y la compilación nativa utilizadas.
npm ci
npm run build
npx cap sync android
cd android
./gradlew bundleRelease
Mantenga la siguiente información en el mismo lugar para cada versión:
product version: 1.4.0
android versionCode: 10400
web commit: 8f3c1a2
capacitor sync: completed
artifact: app-release.aab
api compatibility: 1.2.0+
Las actualizaciones dentro de la aplicación de Android son una forma de notificar a los usuarios sobre las actualizaciones mientras están dentro de la aplicación. Los flujos flexibles permiten descargas en segundo plano y gestión de estado, mientras que los flujos inmediatos pueden solicitar transiciones más sólidas. Cualquiera de los flujos debe diseñarse para que no se pierdan las tareas del usuario. Si tiene un artículo extenso que está escribiendo o un archivo que está cargando, primero debe determinar si forzar una actualización ahora es lo correcto para el producto.
// 네이티브 Android 레이어에서 업데이트 가능 여부를 확인하는 형태의 예시
val appUpdateManager = AppUpdateManagerFactory.create(context)
appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
// UI는 "다운로드 중에도 계속 작업할 수 있음"을 설명해야 합니다.
appUpdateManager.startUpdateFlowForResult(
info,
AppUpdateType.FLEXIBLE,
activity,
REQUEST_CODE
)
}
}
Lo importante aquí es el mensaje y no la llamada API. Si simplemente aparece el mensaje "Hay una actualización disponible", el usuario podría perder su trabajo. Para actualizaciones flexibles, la interfaz de usuario debe indicar si el trabajo puede continuar durante la descarga, si es necesario reiniciar antes de la instalación o si se puede realizar más tarde. Si está escribiendo una actualización de inmediato, asegúrese de que sus usuarios comprendan por qué es necesaria ahora.
Criterios para manejar actualizaciones de activos web en Capacitor
La tentación de cambiar rápidamente las propiedades web es grande. Sin embargo, si los cambios en los recursos web dentro de una aplicación se entrelazan con el código ejecutable, el comportamiento de la aplicación, los pagos y los permisos, cuanto más se traten como “cambios de contenido”, más peligrosos se volverán. Las políticas de la plataforma y las perspectivas de revisión pueden cambiar en cualquier momento, por lo que cualquier posibilidad de cambio remoto debe verificarse con las últimas políticas de la tienda y una revisión legal/de seguridad justo antes del lanzamiento.
En la práctica, la siguiente línea es segura:
| cambiar | Principios operativos |
|---|---|
| Lista de frases, imágenes y servidores. | Gestionado por cambios de servidor e invalidación de caché. |
| Condiciones de exposición del experimento. | Administre con configuraciones remotas pero tenga valores predeterminados y desconexión automática |
| Estructura de pantalla, navegación, autenticación, flujo de pago. | Administrar con lanzamientos de aplicaciones |
| Complemento nativo·Permiso·SDK | Administrar con lanzamientos de aplicaciones |
| Formato de base de datos/reglas de sincronización fuera de línea | Administre los lanzamientos de aplicaciones con planes de migración y recuperación |
Esta tabla es un límite de responsabilidad, no una limitación técnica. Los cambios que hacen que los usuarios se comporten de manera diferente a la aplicación que instalaron desde la tienda deben tratarse de manera más conservadora, incluso en el contexto de pruebas, revisión y atención al cliente. Si necesita una solución rápida, considere "¿A quién y cómo revertiré este cambio si falla?" en lugar de "¿Puedo realizar el cambio de forma remota?".
La configuración remota debe tener versiones al mismo nivel que las aplicaciones.
{
"schemaVersion": 3,
"minimumAppVersion": "1.3.0",
"featureFlags": {
"newEditor": false,
"offlineSearch": true
},
"fallback": {
"newEditor": false,
"offlineSearch": false
}
}
Su aplicación debería recurrir a valores predeterminados seguros en caso de fallas de red, JSON no válido o esquemas desconocidos. El momento en que enciendes el interruptor es más importante que el momento en que lo apagas. El interruptor de apagado no debería estar sólo en la consola; Debería estar en la documentación operativa quién lo apaga y bajo qué circunstancias.
Tauri Las actualizaciones hacen que la firma sea clave para la continuidad del producto.
El actualizador Tauri v2 utiliza firmas para verificar que los archivos de actualización provienen de una fuente confiable. La clave pública ingresa a la configuración de la aplicación y la clave privada se usa para firmar artefactos de liberación. Es especialmente importante que si pierde su clave privada, no podrá publicar nuevas actualizaciones para las aplicaciones que ya están instaladas. Esto no es sólo un secreto de CI, es la clave que vincula su producto instalado con la próxima versión.
La configuración inicial convierte la generación de claves, el almacenamiento CI, los permisos de acceso y la respuesta a incidentes en una sola lista de verificación.
# 개발자가 개인 키를 생성하는 예시. 실제 경로·보관 정책은 팀의 비밀관리 체계를 따릅니다.
npm run tauri signer generate -- -w ./secrets/my-app.key
# CI에서는 저장소 파일이 아니라 보호된 secret을 환경 변수로 주입합니다.
export TAURI_SIGNING_PRIVATE_KEY="$TAURI_SIGNING_PRIVATE_KEY"
npm run tauri build
{
"bundle": {
"createUpdaterArtifacts": true
},
"plugins": {
"updater": {
"pubkey": "앱에 포함할 공개 키",
"endpoints": [
"https://updates.example.com/{{target}}/{{arch}}/{{current_version}}"
]
}
}
}
La URL de ejemplo y la cadena de clave no son configuraciones que se puedan copiar tal como están. Pero la estructura es clara. La aplicación recibe información de la nueva versión desde un punto final HTTPS y verifica el resultado con una firma. Ya sea que opere el servidor de actualización con JSON estático o respuestas dinámicas, el control de acceso al servidor y los permisos de distribución deben administrarse al mismo nivel que los permisos de distribución de aplicaciones.
La respuesta de actualización contiene la versión, la ubicación de descarga, la firma y los cambios. No es sólo la ruta normal de “hay una nueva versión” lo que los equipos deben probar.
- ¿No están instaladas las firmas incorrectas?
- ¿Qué política utiliza cuando necesita volver a una versión anterior?
- ¿Se puede evitar esto con una respuesta segura cuando los metadatos de actualización se distribuyen incorrectamente?
- Cuando hay un trabajo no guardado por parte del usuario, ¿cuál es el orden de descarga y reinicio?
- ¿Todas las salidas para cada plataforma tienen el mismo rango de compatibilidad de productos?
Electron opera el servidor de actualización y el perímetro de seguridad juntos
autoUpdater de Electron comprueba las actualizaciones y admite la descarga e instalación. Sin embargo, la operación difiere según la plataforma y el método de empaquetado, y el alcance del soporte básico integrado y el método de distribución Linux deben verse por separado. Las actualizaciones son un área directamente relacionada con la confianza del usuario, por lo que es difícil dejar que "la herramienta de empaquetado se encargue de ello".
En su forma más pequeña, el código de verificación de actualizaciones podría verse así:
// electron/main.ts
import { app, autoUpdater, BrowserWindow } from 'electron';
autoUpdater.setFeedURL({
url: 'https://updates.example.com/feed'
});
autoUpdater.on('update-available', () => {
BrowserWindow.getAllWindows()[0]?.webContents.send(
'update:status',
{ state: 'downloading' }
);
});
autoUpdater.on('update-downloaded', (_event, notes, name) => {
BrowserWindow.getAllWindows()[0]?.webContents.send(
'update:status',
{ state: 'ready', notes, name }
);
});
// 사용자가 "재시작하고 설치"를 명시적으로 선택했을 때만 호출합니다.
function installDownloadedUpdate() {
autoUpdater.quitAndInstall();
}
Aquí quitAndInstall() es técnicamente una línea, pero en producción se necesita una pantalla. Debe explicar qué perderá el usuario, cuándo se volverá a abrir y si se pueden retrasar las actualizaciones. La idea de que las actualizaciones automáticas más silenciosas son mejores también es peligrosa. Necesitamos distinguir entre casos urgentes, como parches de seguridad, y casos que deben esperar el contexto del usuario, como cambios de funciones.
En las aplicaciones Electron, las actualizaciones en sí mismas no deberían ser una excusa para otorgar permisos arbitrarios al renderizador. IPC, que muestra el estado de actualización, debe restringirse a eventos de solo lectura y no debe permitir que la pantalla cambie arbitrariamente la URL de descarga o el sistema de archivos. Los estrechos límites entre los procesos principales y de precarga se aplican no solo al desarrollo de funciones sino también a las operaciones de actualización.
Al dividir los canales, se puede distinguir entre distribución de emergencia y distribución imprudente.
Los canales de lanzamiento no son sólo para grandes empresas. Incluso si forma un equipo de dos personas, puede reducir el radio de accidentes de distribución dividiendo solo stable y preview.
| canal | Objetivo | riesgo tolerable | observar |
|---|---|---|---|
| internal | Equipo/Probador | La mayoría de los errores funcionales distintos de la eliminación de datos | Instalación/Inicio de sesión/Migración |
| preview | Adoptador temprano voluntario | Problemas de interfaz de usuario/rendimiento/compatibilidad | Fallo, abandono y contacto de soporte |
| stable | todos los usuarios | Solo se permite riesgo bajo | Tasa de error, tasa de finalización de actualizaciones, acciones clave |
| hotfix | Usuarios afectados | Solución de regresión clara | Recuperación exitosa y prevención de recurrencia. |
Aquí el canal no es solo una cadena de versión. Es un dispositivo operativo que pasa primero la migración de la base de datos a través de la vista previa, determina el período durante el cual el servidor estable también puede leer aplicaciones anteriores y determina de antemano si se deben desactivar las banderas del servidor, actualizar el feed o almacenar la distribución al retroceder.
Los números de versión también se separan en nombres específicos del usuario y específicos del dispositivo.
사용자에게 보이는 버전: 1.4.0
Android versionCode: 10400
데스크톱 업데이트 채널: stable
서버 API 최소 호환: 1.2.0
데이터 스키마: 7
Si estos cinco valores se sellan juntos en las notas de la versión, los artefactos CI y los informes de errores, puede limitar "qué versión solo ocurre" mucho más rápido. Especialmente para las aplicaciones que combinan web y nativas, es difícil determinar la causa con solo mirar la versión en pantalla.
La automatización no es un botón de despliegue, sino un flujo que deja evidencia
El proceso de lanzamiento no está completo solo porque CI ejecuta el comando build y carga el resultado. Más tarde, cuando ocurre una falla, el equipo necesita saber "qué fuente se firmó con qué clave y se distribuyó a quién". Es decir, la salida de la automatización no es solo .ipa, .aab, .dmg, .exe, sino también prueba de distribución.
Como mínimo, deje la siguiente información en un JSON o una nota legible por humanos por versión:
{
"release": "1.4.0",
"channel": "preview",
"commit": "8f3c1a2",
"builtAt": "2026-08-22T12:00:00Z",
"artifacts": [
{ "platform": "android", "versionCode": 10400, "file": "app-release.aab" },
{ "platform": "macos", "file": "my-app.app.tar.gz" },
{ "platform": "windows", "file": "my-app-setup.exe" }
],
"minimumApiCompatibility": "1.2.0",
"migration": "schema-7"
}
Los valores en sí son importantes porque conectan unos con otros. En atención al cliente, si el usuario dice 1.4.0, debería poder encontrar qué confirmación y esquema de datos es. Si el problema son los metadatos incorrectos en su fuente de actualización, debe poder detener inmediatamente qué salida estuvo expuesta a qué canal. También puede verificar que la distribución que está procesando la tienda y el instalador de escritorio que descarga asumen directamente los mismos cambios API.
La automatización también requiere una separación entre “emisión” y “aprobación”. No permita que cada PR sobrescriba el feed de actualización estable, solo publique resultados de prueba en canales internos y cree estable solo en etiquetas donde haya revisado cambios, migraciones y planes de reversión. El acceso a claves privadas y certificados de almacenamiento requiere una mayor limitación. Verifique periódicamente que los valores secretos no estén registrados en el registro de compilación y que las claves no queden en el directorio local.
# CI 의사 코드: 실제 공급자 문법에 맞게 구현한다.
release:
needs: [test, build]
if: startsWith(git.ref, 'refs/tags/v')
steps:
- verify: version and migration plan
- sign: inject protected credentials only for this job
- publish: upload to preview or stable by approved channel
- record: attach release manifest and checksums
Este flujo puede parecer lento, pero es el más rápido para las revisiones. Esto se debe a que ya tenemos una idea de quién puede cambiar el feed, qué versiones están disponibles y qué candidatos se pueden revertir de forma segura. La rapidez no pasa por eliminar el paso de aprobación. Surge al reducir la incertidumbre.
Revertir es más amplio que volver a cargar un archivo anterior
La recuperación no termina simplemente apagando la nueva versión. Esto puede deberse a que una nueva aplicación ya almacenó los datos en un nuevo formato, es posible que el servidor haya eliminado campos antiguos o que se hayan almacenado en caché indicadores de funciones. En realidad, deberías practicar la siguiente secuencia una vez antes de la implementación.
1. 영향 범위를 확인한다: 어떤 플랫폼·버전·계정이 문제인가
2. 확산을 멈춘다: 스토어 단계 배포 중지, 업데이트 피드 보류, 플래그 OFF
3. 안전 모드로 전환한다: 쓰기 기능을 막고 읽기·내보내기를 우선한다
4. 데이터 호환을 확보한다: 이전 앱이 읽을 응답과 포맷을 되살린다
5. 복구 버전을 낸다: 새 버전의 목적과 복구 조건을 릴리스 노트에 적는다
6. 사후 검토한다: 어떤 신호가 더 일찍 이 문제를 알려줬어야 하는가
Las mejores reversiones no son las que no es necesario hacer. Los usuarios no pierden su trabajo si algo sale mal, el equipo sabe qué hacer a continuación y fallas similares se detectan antes en el canal. Desde esta perspectiva, las actualizaciones de aplicaciones, las copias de seguridad de datos, el monitoreo de errores y las cartas de atención al cliente son un solo sistema.
La lista de verificación de distribución debe cerrarse mediante planificación, diseño y desarrollo.
Por último, es posible que desee copiar la lista de verificación a continuación en la documentación de Notion o relaciones públicas de su lanzamiento.
Producto y Diseño
- ¿Escribiste en una oración qué cambia este cambio en el flujo de usuarios?
- [] ¿Hay pantallas y texto para la descarga de actualizaciones, el reinicio, el error y el estado de permiso denegado?
- [] ¿Los cambios son comprensibles para los usuarios de aplicaciones más antiguas?
- [] ¿Puede el revisor de la tienda reproducir la funcionalidad principal?
Desarrollo y Seguridad
- [] ¿Grabó el SHA de compilación web, la versión nativa y el hash de salida?
- [] ¿El ID de la aplicación, la clave de firma y el código de versión coinciden con la política de lanzamiento?
- ¿Ha restringido los permisos de la clave privada de actualización Tauri o del feed de actualización Electron a un número mínimo de personas?
- [] ¿Los permisos del complemento de comando IPC están abiertos solo al rango requerido para la función?
- ¿Se ha determinado el período de compatibilidad para la aplicación anterior y el formato de datos del servidor API·?
Operaciones y soporte
- [] ¿Dónde lo exportas entre interno/vista previa/estable?
- ¿Ha anotado qué conmutador, alimentación y distribución en tienda se detendrá primero si falla?
- [] ¿Ha probado rutas de recuperación para trabajos no guardados y migración de datos?
- [] ¿Están preparadas las guías de versión/reinicio/recuperación actuales para responder las consultas de los usuarios?
Las aplicaciones que se actualizan sin problemas no son sólo las que lanzan nuevas funciones con mayor frecuencia, sino también las que no se olvidan de los usuarios de versiones anteriores. En Capacitor, el límite entre los archivos binarios web y de tienda se debe diseñar en conjunto, en Tauri, la continuidad de la clave de firma y los artefactos de actualización, y en Electron, el empaquetado, la fuente de actualización y la seguridad de IPC se deben diseñar en conjunto. Al separar estos tres ejes antes del lanzamiento, el próximo lanzamiento será más rápido y menos riesgoso.