3.1.1: la versión que enseña Windows no depende de los accesos directos
Al actualizar a la 3.1.0 la instalación de Munka por su propio botón, Configuración › Aplicaciones siguió diciendo 3.0.0. Tras actualizarse se reescribían los dos accesos directos (uno vive en el Escritorio de OneDrive) antes de apuntar la versión, y si uno no se dejaba reescribir, la versión no se apuntaba. instalar.al_dia ya no toca un acceso que abre lo que debe, uno que no se deja no impide lo demás, y la versión se apunta siempre. Con prueba. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Este commit está contenido en:
@@ -735,6 +735,14 @@ que viaja con cada versión; una prueba comprueba que cada pestaña tiene su apa
|
|||||||
Pruebas: `probar.py` 19 de 19, `prueba_funcional.py` 26 de 26, `prueba_ventana.py` 18 desde el código y,
|
Pruebas: `probar.py` 19 de 19, `prueba_funcional.py` 26 de 26, `prueba_ventana.py` 18 desde el código y,
|
||||||
con el paquete, 8 del arranque y 9 de la instalación.
|
con el paquete, 8 del arranque y 9 de la instalación.
|
||||||
|
|
||||||
|
3.1.1, la versión que enseña Windows no depende de los accesos directos. Al actualizar la instalación de
|
||||||
|
Munka a la 3.1.0 por su propio botón, todo fue bien menos un detalle: Configuración › Aplicaciones siguió
|
||||||
|
diciendo 3.0.0. No se llegó a ver el error (sin consola no queda escrito), pero entre notar la versión vieja y
|
||||||
|
apuntar la nueva solo había una cosa: reescribir los dos accesos directos, y uno vive en el Escritorio de
|
||||||
|
OneDrive. Si uno no se dejaba reescribir en ese momento, la versión no se apuntaba; repetido a mano un rato
|
||||||
|
después, y en el equipo de ensayo, funcionó. Ahora `instalar.al_dia` no toca un acceso que ya abre lo que
|
||||||
|
debe, uno que no se deja no impide lo demás, y la versión se apunta siempre. Con prueba.
|
||||||
|
|
||||||
Probado el 9 sep 2026 sobre una copia de ensayo: plan → aplicar → deshacer → volver a aplicar deja el árbol
|
Probado el 9 sep 2026 sobre una copia de ensayo: plan → aplicar → deshacer → volver a aplicar deja el árbol
|
||||||
exacto; conversión ffmpeg verificada y reemplazo con original a `_Para borrar`. El puente de Resolve conectó
|
exacto; conversión ffmpeg verificada y reemplazo con original a `_Para borrar`. El puente de Resolve conectó
|
||||||
con Resolve Studio 21.1 pero no pudo abrir proyecto porque Resolve estaba en el Gestor de proyectos:
|
con Resolve Studio 21.1 pero no pudo abrir proyecto porque Resolve estaba en el Gestor de proyectos:
|
||||||
|
|||||||
45
instalar.py
45
instalar.py
@@ -274,23 +274,24 @@ def instalado_aqui(base):
|
|||||||
return bool(i) and os.path.normcase(os.path.normpath(i["base"])) == os.path.normcase(os.path.normpath(base))
|
return bool(i) and os.path.normcase(os.path.normpath(i["base"])) == os.path.normcase(os.path.normpath(base))
|
||||||
|
|
||||||
|
|
||||||
def instalar(base, version, solo_los_que_hay=False):
|
def _apuntar(base, version):
|
||||||
"""Da de alta el programa de `base`. Con `solo_los_que_hay` no repone un acceso directo que alguien
|
"""La línea del programa en Configuración › Aplicaciones."""
|
||||||
quitó a propósito: solo pone al día los que siguen ahí (es lo que se hace tras cada actualización)."""
|
|
||||||
exe, args = orden(base)
|
exe, args = orden(base)
|
||||||
icono = os.path.join(base, "static", "organilab.ico")
|
|
||||||
hechos = []
|
|
||||||
for ruta in _accesos():
|
|
||||||
if solo_los_que_hay and not os.path.exists(ruta):
|
|
||||||
continue
|
|
||||||
hechos.append(acceso_directo(ruta, exe, args, base, icono))
|
|
||||||
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, CLAVE) as k:
|
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, CLAVE) as k:
|
||||||
for nombre, valor in (("DisplayName", NOMBRE), ("DisplayVersion", version), ("Publisher", "Labstream"),
|
for nombre, valor in (("DisplayName", NOMBRE), ("DisplayVersion", version), ("Publisher", "Labstream"),
|
||||||
("DisplayIcon", icono), ("InstallLocation", base),
|
("DisplayIcon", os.path.join(base, "static", "organilab.ico")), ("InstallLocation", base),
|
||||||
("UninstallString", f'"{exe}" {args} desinstalar'.replace(" ", " "))):
|
("UninstallString", f'"{exe}" {args} desinstalar'.replace(" ", " "))):
|
||||||
winreg.SetValueEx(k, nombre, 0, winreg.REG_SZ, valor)
|
winreg.SetValueEx(k, nombre, 0, winreg.REG_SZ, valor)
|
||||||
for nombre, valor in (("NoModify", 1), ("NoRepair", 1), ("EstimatedSize", _peso_kb(base))):
|
for nombre, valor in (("NoModify", 1), ("NoRepair", 1), ("EstimatedSize", _peso_kb(base))):
|
||||||
winreg.SetValueEx(k, nombre, 0, winreg.REG_DWORD, valor)
|
winreg.SetValueEx(k, nombre, 0, winreg.REG_DWORD, valor)
|
||||||
|
|
||||||
|
|
||||||
|
def instalar(base, version):
|
||||||
|
"""Da de alta el programa de `base`: sus dos accesos directos y su línea en Aplicaciones."""
|
||||||
|
exe, args = orden(base)
|
||||||
|
icono = os.path.join(base, "static", "organilab.ico")
|
||||||
|
hechos = [acceso_directo(ruta, exe, args, base, icono) for ruta in _accesos()]
|
||||||
|
_apuntar(base, version)
|
||||||
return {"base": base, "accesos": hechos, "orden": f'"{exe}" {args}'.strip()}
|
return {"base": base, "accesos": hechos, "orden": f'"{exe}" {args}'.strip()}
|
||||||
|
|
||||||
|
|
||||||
@@ -307,17 +308,27 @@ def poner(base, version, avance=None):
|
|||||||
|
|
||||||
|
|
||||||
def al_dia(base, version):
|
def al_dia(base, version):
|
||||||
"""Tras una actualización: si este programa está dado de alta, Windows tiene que enseñar la versión
|
"""Tras una actualización: si este programa está dado de alta, Windows tiene que enseñar la versión nueva
|
||||||
nueva y los accesos directos tienen que apuntar a lo que ahora lo abre."""
|
y los accesos directos tienen que apuntar a lo que ahora lo abre. Devuelve True si hubo que cambiar algo."""
|
||||||
i = instalado()
|
i = instalado()
|
||||||
if not i or not instalado_aqui(base):
|
if not i or not instalado_aqui(base):
|
||||||
return False
|
return False
|
||||||
exe, args = orden(base)
|
exe, args = orden(base)
|
||||||
viejos = [a for a in map(leer_acceso, (r for r in _accesos() if os.path.exists(r))) if a]
|
cambio = i["version"] != version
|
||||||
if i["version"] == version and all(os.path.normcase(a["exe"]) == os.path.normcase(exe) and a["args"] == args for a in viejos):
|
for ruta in _accesos():
|
||||||
return False
|
if not os.path.exists(ruta):
|
||||||
instalar(base, version, solo_los_que_hay=True)
|
continue # el que alguien quitó a propósito no se repone
|
||||||
return True
|
try:
|
||||||
|
a = leer_acceso(ruta)
|
||||||
|
if a and _misma(a["exe"], exe) and a["args"] == args:
|
||||||
|
continue # ya abre lo que tiene que abrir: no se toca
|
||||||
|
acceso_directo(ruta, exe, args, base, os.path.join(base, "static", "organilab.ico"))
|
||||||
|
cambio = True
|
||||||
|
except OSError:
|
||||||
|
pass # un acceso que ahora no se deja reescribir no impide lo demás
|
||||||
|
if cambio:
|
||||||
|
_apuntar(base, version)
|
||||||
|
return cambio
|
||||||
|
|
||||||
|
|
||||||
def desinstalar(base=None):
|
def desinstalar(base=None):
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"nombre": "OrganiLAB",
|
"nombre": "OrganiLAB",
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"builtAt": "2026-10-04T17:23:53.000Z",
|
"builtAt": "2026-10-04T17:34:05.000Z",
|
||||||
"archivo": "OrganizadorLabstream-portatil-3.1.0.zip",
|
"archivo": "OrganizadorLabstream-portatil-3.1.1.zip",
|
||||||
"size": 195387012,
|
"size": 195387259,
|
||||||
"sha256": "1c80a369bcdc8f07c5a9db1dec1bd95dcd390a8e65714ec60251388544ee6523",
|
"sha256": "117da3af85e14222893a34c7f992cd7853536bdef81f634b8453742e33aebdde",
|
||||||
"app": {
|
"app": {
|
||||||
"archivo": "OrganizadorLabstream-app-3.1.0.zip",
|
"archivo": "OrganizadorLabstream-app-3.1.1.zip",
|
||||||
"size": 11574433,
|
"size": 11574680,
|
||||||
"sha256": "cedd68ade3f61ccd2958e3578bf46be8b92e20d73aade3b7e455585e53898d3e",
|
"sha256": "05b9e0f5d35ac1244d8b1b0eb6aa7caefc9799b8d910f3d3ad42d8e22e3c3a2e",
|
||||||
"url": "/api/herramientas/OrganizadorLabstream-app-3.1.0.zip"
|
"url": "/api/herramientas/OrganizadorLabstream-app-3.1.1.zip"
|
||||||
},
|
},
|
||||||
"notes": "El programa se instala solo en su sitio definitivo (tu carpeta de programas) y trae un manual dentro, en «Manual». Además, dos copias ya no pueden compartir puerto.",
|
"notes": "El programa se instala solo en su sitio definitivo (tu carpeta de programas) y trae un manual dentro, en «Manual». Dos copias ya no pueden compartir puerto, y tras actualizarse Windows enseña siempre la versión nueva.",
|
||||||
"firma": {
|
"firma": {
|
||||||
"alg": "rsa2048-pkcs1v15-sha256",
|
"alg": "rsa2048-pkcs1v15-sha256",
|
||||||
"valor": "1ca46a8c6af640d62da3b8a97500661ae2d1e5442b66fb9ba9ee5268e3cfd85c1c1b6c2949484742adbdc758788bb5df323654b6a8533dd1ce3a93e9b974b0870bfa7c2f74fec4f6ef922cf1228c2d290a09a54dd62cabe846cdb40ed79276cc36940283e39db57ccc5f392daaf074672546f38cc3342cbc569d76723d365c2807755d95e8f129fb5e309952da27f645ef6130af7abb3f1356f895a92745ae890ad0c5b9d2a2139bc85ea5b66bcd3b2385d74c2d4b2bc438acf474e8dde041951d0c8f9949b39bf214d38ce3f0a80451f9c1681a9e3e53c2d28abf3e570201cc624b248213392ada7c892743a26c2be40d5170149d72d2dd3cd8bd92e3bff5d7"
|
"valor": "09ce52a3885faebe34bbb4a8210f460cfa345da578fcb9e73316aec5bdb55ec9977f6cbb9f3d3f8e8925755d6dce94310219d046210fcbcba082134fd088208a9b830086c3bbcbff6acd04259348b70871c935fceb457362619a406f99da2b8abafd69dba7e1400020b90fd7a211d42e72cd49cd0294fbc8fc30e8fbae0ba431e5881f68a6607de7ae44ceadb5b22b007c7a4e5a95347ebdab5b1a645c4814b51a01f95c57e2c0caf9a5a0ccf35ca2f9f53117788b509ff42f4fd8800fe9f38eecb5ee421f3e6dc366b48d344291b21c1b50677fb50e8621733b30d932e0f055b07571b74a0273e528797ae3853851ce35e0eb9a42fe14ae34ca6ea7a6ff2029"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ import actualizador
|
|||||||
asistente.O = sys.modules[__name__]
|
asistente.O = sys.modules[__name__]
|
||||||
actualizador.O = sys.modules[__name__]
|
actualizador.O = sys.modules[__name__]
|
||||||
|
|
||||||
VERSION = "3.1.0"
|
VERSION = "3.1.1"
|
||||||
|
|
||||||
BASE = os.path.dirname(os.path.abspath(sys.executable if getattr(sys, "frozen", False) else __file__))
|
BASE = os.path.dirname(os.path.abspath(sys.executable if getattr(sys, "frozen", False) else __file__))
|
||||||
# Empaquetado: el programa corre con el motor que viaja en su propia carpeta. OrganizadorLabstream.exe es
|
# Empaquetado: el programa corre con el motor que viaja en su propia carpeta. OrganizadorLabstream.exe es
|
||||||
|
|||||||
16
probar.py
16
probar.py
@@ -150,6 +150,22 @@ class Windows(unittest.TestCase):
|
|||||||
self.assertTrue(instalar.al_dia(base, "3.0.1"))
|
self.assertTrue(instalar.al_dia(base, "3.0.1"))
|
||||||
self.assertEqual(instalar.instalado()["version"], "3.0.1")
|
self.assertEqual(instalar.instalado()["version"], "3.0.1")
|
||||||
self.assertFalse(os.path.exists(escritorio))
|
self.assertFalse(os.path.exists(escritorio))
|
||||||
|
# un acceso directo que no se deja reescribir (pasó con el del Escritorio en OneDrive) no impide
|
||||||
|
# apuntar la versión nueva; y uno que ya abre lo que debe ni se toca
|
||||||
|
open(os.path.join(base, "OrganizadorLabstream.exe"), "wb").close() # ahora el programa se abre con otra cosa
|
||||||
|
de_verdad, intentos = instalar.acceso_directo, []
|
||||||
|
instalar.acceso_directo = lambda *a, **k: (intentos.append(a[0]), (_ for _ in ()).throw(OSError("ocupado")))[1]
|
||||||
|
try:
|
||||||
|
self.assertTrue(instalar.al_dia(base, "3.0.2"))
|
||||||
|
finally:
|
||||||
|
instalar.acceso_directo = de_verdad
|
||||||
|
self.assertEqual(intentos, [inicio])
|
||||||
|
self.assertEqual(instalar.instalado()["version"], "3.0.2")
|
||||||
|
self.assertTrue(instalar.al_dia(base, "3.0.2")) # ahora sí se deja: lo pone al día
|
||||||
|
self.assertTrue(instalar._misma(instalar.leer_acceso(inicio)["exe"], os.path.join(base, "OrganizadorLabstream.exe")))
|
||||||
|
self.assertFalse(instalar.al_dia(base, "3.0.2")) # y ya no queda nada que cambiar
|
||||||
|
os.remove(os.path.join(base, "OrganizadorLabstream.exe"))
|
||||||
|
instalar.al_dia(base, "3.0.2")
|
||||||
# quitarlo desde otra copia del programa no toca lo de esta; desde esta, sí
|
# quitarlo desde otra copia del programa no toca lo de esta; desde esta, sí
|
||||||
self.assertEqual(instalar.desinstalar(tmp)["accesos"], [])
|
self.assertEqual(instalar.desinstalar(tmp)["accesos"], [])
|
||||||
self.assertTrue(instalar.instalado_aqui(base))
|
self.assertTrue(instalar.instalado_aqui(base))
|
||||||
|
|||||||
Referencia en una nueva incidencia
Block a user