From 4f008c558b6f9575cadc186a7bc991e922403ec5 Mon Sep 17 00:00:00 2001 From: Rene Kievits Date: Wed, 26 Aug 2026 12:14:31 +0200 Subject: [PATCH] Add a no-SSH install path: a self-hosted Homebrew Channel repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ares-install needs a working ssh into the TV, and this TV only has root access (no Developer Mode, no ssh currently reachable). The Homebrew Channel's own "Add repository" dialog accepts any URL that returns {"packages": [...]}, which is the same schema its own gen-manifest.js uses per app — confirmed by reading the app's source directly rather than guessing at the format. make-manifest.py now also writes out/repo.json, the existing manifest wrapped in that shape, so publishing is: build the ipk, run the script with --base-url pointing at wherever the release assets will live, attach ipk + icon + repo.json there, then paste the repo.json URL into the TV once. No ares, no ssh, no review queue. Root elevation afterwards is unaffected either way: "Grant root access" in the app calls elevate-service over the Luna bus from inside the running app, which was already ssh-independent. Corrected two inaccuracies in the process: webosbrew/repo doesn't exist (the real project is webosbrew/apps-repo), and the Homebrew Channel has no "install from file" UI — Browse-and-install or the /install Luna service are the only ways in, both of which need a URL, not a local path. Co-Authored-By: Claude Opus 5 --- README.md | 39 +++++++++++++++++++++------------- docs/development.md | 48 ++++++++++++++++++++++++++++++++++++------ tools/make-manifest.py | 31 ++++++++++++++++++++------- 3 files changed, 88 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 8ba9203..0c2da9b 100644 --- a/README.md +++ b/README.md @@ -51,27 +51,36 @@ See [docs/hyperhdr.md](docs/hyperhdr.md) for both, step by step. ## Installing -**From the Homebrew Channel.** Open the Homebrew Channel on the TV, find -*Audio Cap*, install, launch. +This app is not in the official Homebrew Channel repository (that needs a +review submission — see [docs/development.md](docs/development.md)). Two ways +to get it onto the TV instead, neither needing SSH or Developer Mode: -**From an ipk.** Copy the ipk to the TV and install it with the Homebrew -Channel's *Install from file*, or from a workstation: +**Add it as a custom repository (recommended).** The Homebrew Channel can +browse-and-install from any repository URL you give it, not just the official +one. Build the ipk, publish it plus a generated repo index, then add that URL +on the TV: + +```sh +npm install # ares-cli, only needed for step 2 +./tools/docker-build.sh # cross-compile; Linux: ./tools/build.sh native +./tools/build.sh package # -> out/*.ipk +python3 tools/make-manifest.py --base-url +# -> out/repo.json, out/manifest.json +``` + +Attach the ipk, `frontend/assets/icon.png` and `out/repo.json` to a release at +that URL, then on the TV: **Homebrew Channel → ⚙ → Add repository** → paste the +`repo.json` URL → back to Browse → *Audio Cap* → Install. Full walkthrough, +including why the URLs have to match exactly, in +[docs/development.md](docs/development.md#publishing-to-the-homebrew-channel). + +**Install the ipk directly**, if you do have ares-cli talking to the TV (e.g. +LG Developer Mode's ssh on port 9922, or a root SSH server you've enabled): ```sh ares-install --device tv out/org.webosbrew.audiocap_1.0.0_all.ipk ``` -**From source.** The NDK is Linux-only, so on macOS or Windows the compile goes -through Docker: - -```sh -npm install # ares-cli -./tools/docker-build.sh # or ./tools/build.sh native on Linux -./tools/build.sh package # -> out/*.ipk -``` - -See [docs/development.md](docs/development.md) for the rest. - ## First run 1. Launch **Audio Cap** on the TV. diff --git a/docs/development.md b/docs/development.md index 21ef7a5..bed7ee5 100644 --- a/docs/development.md +++ b/docs/development.md @@ -107,22 +107,56 @@ npm run serve # http://localhost:8000 ## Publishing to the Homebrew Channel +Two ways to get an ipk into the Browse tab. Neither one touches `ares-install` +or ssh — the app installs itself once the TV can reach a URL. + +### Your own repository (no review, no waiting) + +The Homebrew Channel's *Settings → Repositories → Add repository* accepts any +URL that returns `{"packages": [...]}`, where each entry is the same manifest +[`make-manifest.py`](../tools/make-manifest.py) already writes. Point one at +your own git host's release assets and the app shows up in Browse with no +submission process at all — this is what `--repo-out` (on by default) is for. + 1. Bump `version` in `frontend/appinfo.json`, `servicefiles/package.json` and `package.json`. -2. `./tools/build.sh` and test the ipk on a real set. -3. Attach the ipk and `frontend/assets/icon.png` to a release. -4. Generate the manifest and attach that too: +2. Build the ipk: `./tools/docker-build.sh && ./tools/build.sh package` (or + `./tools/build.sh` on Linux with the NDK installed). +3. Create a release tagged e.g. `v1.0.0` and attach three files to it: the + ipk, `frontend/assets/icon.png`, and a repo index generated with + `--base-url` set to that release's asset URL: ```sh python3 tools/make-manifest.py \ --base-url https://git.crylia.de/Crylia/lgtv_audio_cap/releases/download/v1.0.0 + # -> out/manifest.json (one app entry) + # -> out/repo.json (that entry wrapped as {"packages": [...]}) ``` -5. Submit the manifest URL to [webosbrew/repo](https://github.com/webosbrew/repo). + Attach `out/repo.json` itself too — its own download URL is what you paste + into the TV, and it must match `--base-url` exactly or the ipk/icon links + inside it point at the wrong place. +4. On the TV: Homebrew Channel → gear icon → *Add repository* → paste the + `repo.json` release URL → back out to Browse → find *Audio Cap* → Install. -The manifest sets `"rootRequired": true`, which tells the Homebrew Channel the -service needs elevating. The app can also do it on demand — *System → Grant root -access* runs the Channel's `elevate-service`. +Shipping an update later is the same four steps with a bumped version and a +new release tag; the Channel diffs against the installed version and offers +an update once the repo URL is already added. + +### The official repo (public listing, reviewed) + +Submit once to [webosbrew/apps-repo](https://github.com/webosbrew/apps-repo) +and anyone with a stock Homebrew Channel install can find it without adding a +custom repository. That project wants a `.yml` descriptor pointing at your own +hosted `manifest.json` (the un-wrapped file from step 3 above) — see its README +for the submission format. + +--- + +`rootRequired: true` in the manifest tells the Homebrew Channel the service +needs elevating, from either path. The app can also do it on demand — +*System → Grant root access* runs the Channel's `elevate-service` itself, over +the Luna bus, so it works whether SSH is enabled on the TV or not. ## How the service is put together diff --git a/tools/make-manifest.py b/tools/make-manifest.py index 194063d..270ade3 100755 --- a/tools/make-manifest.py +++ b/tools/make-manifest.py @@ -1,15 +1,21 @@ #!/usr/bin/env python3 -"""Writes the Homebrew Channel manifest for a built ipk. +"""Writes the Homebrew Channel manifest for a built ipk, and a custom-repo +index that wraps it. -The Homebrew Channel installs apps from a manifest that points at the ipk and -carries its hash. Publishing means putting this file somewhere stable (a GitHub -release asset, or the repository itself) and submitting the URL to -webosbrew/repo. +The manifest (out/manifest.json) is one app entry: id, ipkUrl, ipkHash, and so +on. It is what you submit to webosbrew/apps-repo to get into the official +store. + +The repo index (out/repo.json) is that same entry wrapped as +`{"packages": [...]}`, which is the format the Homebrew Channel's own +"Add repository" dialog expects (Settings -> Repositories -> Add repository). +Host it anywhere static, paste its URL in, and the app shows up in Browse — +no submission, no review, no shell access to the TV at all. python3 tools/make-manifest.py \\ - --base-url https://github.com/you/lgtv-audio-cap/releases/download/v1.0.0 + --base-url https://git.example/you/lgtv-audio-cap/releases/download/v1.0.0 -Defaults to the newest ipk in out/ and writes out/manifest.json. +Defaults to the newest ipk in out/ and writes out/manifest.json + out/repo.json. """ import argparse @@ -47,6 +53,8 @@ def main(): parser.add_argument("--source-url", default=SOURCE_URL, help="project page shown in the Homebrew Channel") parser.add_argument("--out", default=os.path.join(ROOT, "out", "manifest.json")) + parser.add_argument("--repo-out", default=os.path.join(ROOT, "out", "repo.json"), + help="path for the custom-repo index (set to '' to skip)") args = parser.parse_args() appinfo = json.load(open(os.path.join(ROOT, "frontend", "appinfo.json"))) @@ -79,9 +87,16 @@ def main(): with open(args.out, "w") as fh: json.dump(manifest, fh, indent=2) fh.write("\n") - print(json.dumps(manifest, indent=2)) print("\nwrote %s" % os.path.relpath(args.out, ROOT), file=sys.stderr) + + if args.repo_out: + os.makedirs(os.path.dirname(args.repo_out) or ".", exist_ok=True) + with open(args.repo_out, "w") as fh: + json.dump({"packages": [manifest]}, fh, indent=2) + fh.write("\n") + print("wrote %s" % os.path.relpath(args.repo_out, ROOT), file=sys.stderr) + if not base: print("note: no --base-url given, so ipkUrl and iconUri are relative", file=sys.stderr)