Closing the popup after the fact left it on screen for the 15-20 seconds
between the updater's version check and the Homebrew Channel running its
init.d hooks. That race cannot be won, so remove it.
/usr/sbin/update is launched on demand by ls-hubd via a manifest, and
ls-hubd.conf lists ManifestsVolatileDirectories under /var - writable,
persistent, and how webOS itself ships manifest updates. A manifest there
with the same id and a higher version replaces the read-only one, so drop
in a copy that keeps every role and permission file and only empties
serviceFiles. Nothing on the bus can start the updater after that: no
version check, no alert, nothing to dismiss. Callers get an immediate
"Service does not exist" rather than a hang, and deleting the file undoes it.
Verified on a CX (webOS 5, 04.60.65) across reboots: no update process,
/tmp/var/log/update.log never created, no alert. Previously that log was
55 kB with two server checks and an _gAlertWindowId per boot. Revert puts
the updater back on the bus.
The manifest, its D-Bus service file and the updater binary are all
discovered from ls-hubd.conf rather than hardcoded. The popup-closing
layer stays as a fallback for when this one is off.
Also tried and rejected, now documented in the README: update-related
settings, masking update.service (no writable unit directory), and
breaking its ping through /var/systemd/system/env/update.env - the ping
does fail, but something else on the bus activates the updater anyway.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking snu.lge.com in /etc/hosts never stopped the boot popup on a CX,
and the reason only showed up on the TV itself: /usr/sbin/update is a
systemd unit (webos-mbd.target) that runs its version check against
https://snu.lge.com/CheckSWAutoUpdate.laf 15-20 seconds before the
Homebrew Channel gets as far as running its init.d hooks. The check
therefore succeeds on every boot and the alert is already on screen
before any hosts entry exists. Homebrew Channel's own "block system
updates" toggle loses the same race.
Nothing running that late can win it: every systemd unit path is a
read-only overlay except tmpfs /run, and no persistent setting gates the
check - automaticUpdate, support/softwareUpdateEnable,
hotelMode/swUpdateEnable and .UpdateIsInprogress were each measured by
restarting the daemon and counting its requests.
So dismiss the popup instead. The boot hook recovers the alert id from
the updater's own log (_gAlertWindowId), which is the only way to reach
an alert that opened before we could subscribe - com.webos.notification
never reports it to a late subscriber and closeAllAlerts rejects every
source id it accepts. A companion alert-watch.sh then stays subscribed
for the rest of the session.
Also drop two things that were never true. There is no staged firmware
image driving the popup (the staging dir is empty at boot; the size the
daemon reports is in-memory only), and there is no update service to
stop - /etc/init is dead upstart leftovers on a systemd TV, so the old
stopServices layer printed "stopped update" while doing nothing.
- rename the hook to 00-lgupdateblocker so run-parts runs it first,
removing the legacy file on apply
- add support/ and hotelMode/ to the scanned settings categories
- stop matching "ota" inside screenRotation, which would have switched
screen rotation off
- kill the watcher by process group, and make its TERM trap exit - a
trap that only returns resumes the script, which then re-subscribes
Verified on an LG OLED55CX8LB (webOS 5, 04.60.65): after a reboot the
boot log records "dismissed update popup
com.webos.service.update-1788650782451", matching the id the updater
logged that boot. Raised 01:26:22, closed 01:26:37 - so it is visible
for ~15s and then goes away on its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The workflow was written for github.com: actions/upload-artifact failed on
the Gitea runner, softprops/action-gh-release only talks to the GitHub API,
and gen-manifest.js hardcoded github.com URLs.
- drop the artifact upload (release assets are the deliverable)
- upload release assets with tools/release-gitea.js, a dependency-free
Gitea API client; re-running a tag replaces the attachments
- gen-manifest.js takes --server/PUBLIC_SERVER_URL and knows that Gitea
spells the latest-asset URL /releases/download/latest/<file>
- PUBLIC_SERVER_URL is set explicitly because the runner only sees the
instance's internal LAN address
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>