El build NUNCA firmó la app de macOS. Al no pasarle ninguna identidad, el
bundler de Tauri se saltaba `codesign` entero y el .dmg salía con la firma
ad hoc que deja el enlazador:
Identifier=labstream_desktop-0a5819ba37119701
flags=0x20002(adhoc,linker-signed)
Info.plist=not bound
Sealed Resources=none
Tres cosas mal, y ninguna avisaba en ningún log:
· El Entitlements.plist del repo era DECORACIÓN. Los entitlements se
escriben al firmar; sin firma, el micrófono, la cámara y el JIT nunca
pidieron nada porque nunca estuvieron pedidos.
· Ni el Info.plist ni los recursos iban dentro de la firma: se podían
cambiar sin romperla.
· El identificador era el del enlazador y no `co.labstream.os`, que es por
el que macOS recuerda los permisos que le concedes a una app.
AHORA. `APPLE_SIGNING_IDENTITY` se calcula en un paso propio y no en el
`env:` del build, porque un secret que no existe llega como cadena VACÍA y
Tauri lee `APPLE_CERTIFICATE=""` como «hay certificado» y revienta al
importarlo. Solo se exporta lo que tiene valor:
· con Developer ID → firma real, y notarizado si están las tres claves;
· sin él → identidad "-", firma AD HOC. No engaña a Gatekeeper, pero sella
los recursos, ata el Info.plist y APLICA LOS ENTITLEMENTS. En cuanto
Jonathan añada los secrets, la siguiente etiqueta sale firmada de verdad
sin tocar un solo archivo.
ENSAYADO sobre una copia de la app instalada, con el mismo comando que va a
correr el CI. Antes y después:
flags=0x20002(adhoc,linker-signed) → flags=0x10002(adhoc,runtime)
Identifier=labstream_desktop-0a58… → Identifier=co.labstream.os
Info.plist=not bound → Info.plist entries=17
Sealed Resources=none → Sealed Resources version=2 rules=13
(sin entitlements) → allow-jit, audio-input, camera
Y EL ENSAYO ENCONTRÓ UNA TRAMPA que `plutil -lint` no ve: el
Entitlements.plist llevaba `--entitlements` dentro de un comentario XML, y
dos guiones seguidos ahí son XML inválido. plutil lo aceptaba; AMFI, que es
quien lo lee de verdad dentro de codesign, contestaba «Failed to parse
entitlements» y la firma fallaba. Queda avisado en el propio archivo.
De paso se cae `com.apple.security.network.client`: es exclusiva del App
Sandbox y esta app no lo usa, así que no hacía nada.
LA RED QUE FALTABA: un paso nuevo comprueba la firma del .app y TUMBA la
publicación si los entitlements no llegaron. Que esto estuviera roto no lo
dijo nada durante meses; se descubrió a mano persiguiendo otra cosa.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verificar que compila no basta para un cambio que se ve: la barra de pestañas hay
que MIRARLA. El job nuevo construye el .exe de Windows y lo cuelga de la ejecución
como artifact, sin tocar Releases ni el updater. Así se puede probar el cambio en la
app de verdad antes de publicárselo al equipo, que en la práctica no tiene vuelta
atrás porque las apps instaladas se actualizan solas.
Firma con la clave del repo porque tauri.conf.json pide artefactos de updater; el
.exe se queda en el artifact igualmente.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
En Windows la ventana conservaba la barra de título del sistema y la barra de
pestañas iba DEBAJO: una fila entera gastada en repetir el nombre de la app. En
macOS ya no pasaba (TitleBarStyle::Overlay desde la v1.4.2). Ahora las dos se
comportan igual, y como Chrome: la barra de pestañas ES la fila del título.
- Ventana sin decoración en Windows (decorations(false)). resizable(true) conserva
WS_THICKFRAME, así que los bordes siguen redimensionando y Aero Snap (arrastrar a
un borde, Win+flechas) sigue vivo.
- La barra dibuja sus propios botones minimizar/maximizar/cerrar con las medidas y
colores del sistema (46 px de ancho, alto completo de la barra, esquinas rectas,
rojo #c42b1c al posarse en el aspa) y los glifos de Segoe Fluent Icons. Actúan
por el evento nuevo ls-window; cerrar sigue pasando por CloseRequested, o sea que
oculta a la bandeja igual que antes.
- El icono del medio alterna maximizar/restaurar según el estado. El repintado se
dispara solo cuando ese estado CAMBIA, no en cada píxel del redimensionado.
- La geometría maximizada ya no se guarda entre sesiones: sin decoración, al
reabrir volvería como ventana normal del tamaño de la pantalla y taparía la barra
de tareas. Se conserva la última geometría restaurada.
- Doble clic en el hueco libre de la barra ahora maximiza/restaura (lo gobierna
data-tauri-drag-region), como la barra de título del sistema y como Chrome. Antes
abría una pestaña nueva; para eso quedan el botón + y Cmd/Ctrl+T.
NO incluye los Snap Layouts de Windows 11 (el menú que asoma al posar el cursor
sobre maximizar). Requieren que la ventana conteste WM_NCHITTEST con HTMAXBUTTON, y
aquí ese botón lo tapa el webview hijo de la barra, que se queda con los mensajes
del ratón: la ventana principal nunca los ve. Es la misma razón por la que Electron
necesita botones NATIVOS (titleBarOverlay) para poder ofrecerlo. El resto de Aero
Snap no se ve afectado.
Se añade .github/workflows/check.yml: cargo check en Windows y macOS al empujar una
rama, sin publicar nada. Hasta ahora solo existía el workflow de publicar (etiquetas
v*), así que no había forma de comprobar que algo compilaba sin sacar un release.
Verificado: sintaxis del script de la barra con node --check; medidas, clases y
estados de los botones comprobados en el navegador (46 px, pegados al borde, ocultos
en macOS, icono y rótulo correctos al maximizar, y el arrastre solo en el hueco
libre, nunca en los botones). La compilación la verifica el workflow nuevo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Al abrir, la app busca en segundo plano una versión nueva en los Releases de GitHub
(latest.json), y si la hay —firmada con nuestra clave— la instala sola y reinicia.
- Cargo/lib.rs: tauri-plugin-updater + check_update() en el arranque (no bloquea).
- tauri.conf.json: bundle.createUpdaterArtifacts=true y plugins.updater con el endpoint
(releases/latest/download/latest.json) y la clave PÚBLICA de verificación.
- CI: build.yml pasa a tauri-apps/tauri-action, que construye el .exe (Windows) y el
.dmg universal (macOS), firma los artefactos del updater y publica todo —incluido
latest.json— en un Release. Requiere los secrets TAURI_SIGNING_PRIVATE_KEY y
TAURI_SIGNING_PRIVATE_KEY_PASSWORD (la clave privada NO va en el repo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Workflow en matriz que genera .exe (NSIS) y .dmg (Intel+Apple Silicon)
en cada versión. targets=all en la config y selección por --bundles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cliente delgado que abre os.labstreamsas.com en su propia ventana.
Incluye build por GitHub Actions y guía de versionado.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>