mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 06:16:20 -06:00
A BrightSign consultant ran our v1.9.29-rc2 autorun.zip through BSN.cloud's automated deployment. The archive reached the player and then could not be opened — reported as invalid. Two causes, both ours. 1. COMPRESSION. We built with default deflate. The player bootstrap extracts autozip.brs by itself before any script runs, and roBrightPackage supports a specific set of methods, of which "no compression" is the universally safe one. Both builders now store: scripts/build-autorun-zip.sh passes -0, and the server-side package builder used archiver level 9 — maximum deflate — so EVERY self-update package it produced would have failed the same way, silently and in the field. 2. THE UNPACK API. We used roUnzip; BrightSign's own tooling uses roBrightPackage. Converted in autozip.brs and in the self-update path. This is the failure mode worth naming: a compressed package uploads, downloads and deploys perfectly, then fails to open on the player. It reads as a broken deployment rather than a broken zip, so it gets debugged everywhere except where the bug is. Both builders now ASSERT the property rather than trusting the flag — the build script walks `unzip -v` and refuses a compressed member, and a test walks the local file headers of the server-built package checking method 0. Verified by negative control: re-enabling compression fails the test. Also adopted the shipped volume-discovery pattern in autozip.brs — probe USB1:/SD:/SSD:/FLASH: for the archive instead of guessing two volumes. The unit that drove this port boots from FLASH because its card interface is dead, and extracting to a volume that does not exist fails silently. 1056 pass. Reported by giyokun, who was right about both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
97 lines
3.8 KiB
Plaintext
97 lines
3.8 KiB
Plaintext
' ScreenTinker — autorun.zip unpacker.
|
|
'
|
|
' Ships INSIDE autorun.zip, at its root. The card (or internal flash) carries a single file —
|
|
' autorun.zip — and this script unpacks it in place, marks it done so it never re-extracts, and
|
|
' reboots into the real host.
|
|
'
|
|
' That is the whole point of the zip: one file to hand someone, or to drop on a hundred cards,
|
|
' instead of four files that must all arrive intact and in the right place. A partially-copied
|
|
' set of loose files boots into something broken; a partially-copied zip simply fails to extract
|
|
' and leaves the player where it was.
|
|
'
|
|
' ⚠️ autorun.brs must NOT sit next to autorun.zip on the storage root — its presence stops the zip
|
|
' being processed at all. autorun.brs belongs INSIDE the zip, which is where the build script puts
|
|
' it (scripts/build-autorun-zip.sh).
|
|
'
|
|
' Unpacks with roBrightPackage, which is what BrightSign's own tooling uses — NOT roUnzip.
|
|
' A BrightSign consultant flagged this after our first archive failed his automated deployment:
|
|
' the zip reached the player and then could not be opened. Two causes, both fixed:
|
|
' - the archive must be STORED, no compression (scripts/build-autorun-zip.sh now asserts it)
|
|
' - roBrightPackage is the supported reader for a player package
|
|
'
|
|
' Requires BrightSignOS 7.0.60+.
|
|
|
|
' WHERE the archive is. A player may be fed from USB, a card, an SSD, or internal flash — and the
|
|
' unit that drove this port boots from FLASH because its card interface is physically dead. Probing
|
|
' for the file beats assuming a volume: extracting to "SD:/" on a player with no card writes to a
|
|
' volume that does not exist, and the deployment silently does nothing.
|
|
Function SourceRoot() As String
|
|
volumes = ["USB1:", "SD:", "SSD:", "FLASH:"]
|
|
for each v in volumes
|
|
if DoesFileExist(v + "/autorun.zip") then return v
|
|
end for
|
|
return ""
|
|
End Function
|
|
|
|
Sub Main()
|
|
root$ = SourceRoot()
|
|
if root$ = "" then
|
|
print "[st-autozip] no autorun.zip on any volume — nothing to do"
|
|
return
|
|
end if
|
|
zipPath$ = root$ + "/autorun.zip"
|
|
extractPath$ = root$ + "/"
|
|
donePath$ = root$ + "/autorun.zip.done"
|
|
|
|
print "[st-autozip] volume "; root$
|
|
|
|
if not DoesFileExist(zipPath$) then
|
|
print "[st-autozip] no autorun.zip at "; zipPath$; " — nothing to do"
|
|
return
|
|
end if
|
|
|
|
' Idempotence. Without this the player extracts, reboots, extracts again, reboots again —
|
|
' a boot loop that looks like a hardware fault.
|
|
if DoesFileExist(donePath$) then
|
|
print "[st-autozip] already unpacked (autorun.zip.done present) — leaving it alone"
|
|
return
|
|
end if
|
|
|
|
print "[st-autozip] unpacking "; zipPath$
|
|
|
|
package = CreateObject("roBrightPackage", zipPath$)
|
|
if package = invalid then
|
|
print "[st-autozip] ERROR: could not open the archive — is it STORED (no compression)?"
|
|
' Deliberately NOT marking it done: a corrupt, truncated or wrongly-compressed copy should
|
|
' be retried once someone replaces the file, not silently skipped forever.
|
|
return
|
|
end if
|
|
|
|
if not package.Unpack(extractPath$) then
|
|
print "[st-autozip] ERROR: unpack failed"
|
|
return
|
|
end if
|
|
|
|
print "[st-autozip] extracted"
|
|
|
|
fs = CreateObject("roFileSystem")
|
|
if fs = invalid then
|
|
print "[st-autozip] ERROR: no roFileSystem — cannot mark the archive done"
|
|
return
|
|
end if
|
|
|
|
if not fs.Rename(zipPath$, donePath$) then
|
|
print "[st-autozip] ERROR: could not rename the archive; refusing to reboot into a loop"
|
|
return
|
|
end if
|
|
|
|
print "[st-autozip] rebooting into the unpacked player"
|
|
sleep(2000)
|
|
RebootSystem()
|
|
End Sub
|
|
|
|
Function DoesFileExist(filePath$ As String) As Boolean
|
|
files = MatchFiles(filePath$, filePath$)
|
|
return files.Count() > 0
|
|
End Function
|