Skip to content

Building from Source

RadicalMuffinMan edited this page Oct 19, 2026 · 9 revisions

Building from Source

This page takes you from a clean computer to Moonfin packages you can put on a Samsung or LG TV. You don't need Samsung's or LG's development kits to build. Windows, macOS and Linux all work.

For ready-made packages instead, see Installation and Sideloading.

What you need

Tool Version Notes
Node.js The current LTS release (22 or newer) Comes with npm. The builds run on lts/* in CI
Git Any recent version To get the code
A terminal Terminal on macOS and Linux, PowerShell on Windows

That's the whole list. The two build tools the project needs, @enact/cli and @webos-tools/cli, install themselves in the next step.

Step 1: Install Node.js and Git

  • Windows: download the LTS installer from nodejs.org and run it with the defaults. Then install Git for Windows. Open a new PowerShell window afterwards.
  • macOS: with Homebrew, brew install node git. Or use the installers from the same two sites.
  • Linux: your distribution's nodejs, npm and git packages, as long as Node is 22 or newer. nvm or fnm are the easy way to get a current LTS on older distributions.

Check it worked:

node --version
npm --version
git --version

Step 2: Get the code and install

git clone https://github.com/Moonfin-Client/Smart-TV.git
cd Smart-TV
npm install

The repo uses npm workspaces (packages/*), so this one install covers every package. It also installs the two build CLIs into the git-ignored tools/ folder. That takes a minute or two the first time and needs internet.

Step 3: Build

# Samsung: all three variants in one command
npm run build:tizen:all

# Or one at a time
npm run build:tizen          # Regular
npm run build:tizen:oblong   # Oblong (1920x1080 launcher icon)
npm run build:tizen:legacy   # Legacy (Tizen 2.4, no Smart Hub Preview)

# LG
npm run build:webos

# Fire TV (Vega OS), needs the Vega SDK on the PATH for the package step
npm run build:vega

# Xbox, on Windows with the Visual Studio Build Tools and their UWP workload
npm run build:xbox

The packages land in the project root:

  • Samsung: Moonfin_Tizen_<Regular|Oblong|Legacy>_v<version>.wgt
  • LG: Moonfin_webOS_v<version>.ipk
  • Fire TV: Moonfin_Vega_v<version>.vpkg
  • Xbox: Moonfin_Xbox_v<version>.msix, with the dependencies a console needs under packages/build-xbox/shell/Moonfin.Xbox/AppPackages/

They are built exactly the same way as the files on the Releases page. The Samsung package is unsigned, which is what Apps2Samsung expects. See Installation and Sideloading for which variant a TV needs.

Putting a build on your TV

LG

The Developer Mode app on the TV has to be on with Key Server enabled (steps under Installation and Sideloading). Then, once per TV:

npx ares-setup-device

Answer the questions: name the device tv, give the TV's IP address, and paste the passphrase from the Developer Mode app. After that, every build goes on with:

cd packages/build-webos
npm run install-tv    # installs build/*.ipk on the device named tv
npm run launch-tv     # starts it

npx ares-install --device tv Moonfin_webOS_v<version>.ipk from the project root does the same by hand.

Samsung

The simplest way is to feed the .wgt you built to Apps2Samsung, exactly as with a downloaded release. No Samsung tooling needed.

To install straight from the build script instead, you need Samsung's tooling and a certificate for your TV:

  1. Install the Tizen extension for VS Code (Tizen Studio is retired but an existing install works too). It brings the tizen and sdb commands and the Certificate Manager.
  2. Turn on Developer Mode on the TV with your computer's IP (Apps screen, press 1 2 3 4 5), restart the TV, then connect: sdb connect <tv ip>. sdb devices should list it.
  3. Make a certificate with Certificate Manager while the TV is connected, so it can fill in the TV's device id (DUID). Sign in with a Samsung account when asked. Name the profile Moonfin and it drops author.p12 and distributor.p12 into ~/SamsungCertificate/Moonfin/, which is where the build script looks.
  4. Build, sign, install and launch in one go:
cd packages/build-tizen
npm run install-tv

Two things that cost people time: tizen install -t wants the device name from the third column of sdb devices (something like UN32H5000FFXZA), not the ip:port from the first column. And retail TVs refuse most sdb shell commands, so the line successfully launched pid = N is the only confirmation you get from the tools. Look at the TV.

Fire TV (Vega OS)

The web build is done by Enact like the others, then copied into the React Native shell under packages/build-vega/shell/ and packaged with Amazon's Vega SDK. The SDK only supports Ubuntu 20.04 to 24.04 and macOS, so on another distribution run it inside an Ubuntu container (distrobox works). Install it with curl -fsSL https://sdk-installer.vega.labcollab.net/get_vvm.sh | bash and source ~/vega/env. The virtual device cant run WebView apps on Linux, so a stick is the only way to test.

  1. On the stick: Settings > My Fire TV > About, press select seven times on the device name, then Developer options > Developer Mode. Run vega devmode login and vega devmode enable-device --code <the six digits>. Turning Developer Mode off again needs a factory reset.
  2. Developer Options > Connection Mode > TCP/IP, then vega exec vda connect <stick ip>:5555 and export VEGA_SERIAL=<stick ip>:5555.
  3. Build, install and launch:
cd packages/build-vega
npm run launch-tv

node build.js --no-vpkg stops once the web build is in shell/assets/, which is all a machine without the SDK can do. Logs come from vega exec vda shell 'loggingctl log -v org.moonfin.androidtv -f', though only the shell's own console reaches it, not the page's. A Debug build (--debug) exposes Chrome DevTools on port 9229 through vega exec vda forward tcp:9229 tcp:9229, but Debug builds abort after about eight minutes on screen, so test playback on Release.

Xbox

The web build is done by Enact like the others, then copied into the UWP host under packages/build-xbox/shell/Moonfin.Xbox/www/, which serves it to a WebView2 from the package, and the host is built into an .msix with MSBuild. That last step needs Windows with the Visual Studio Build Tools and their Universal Windows Platform workload. node build.js --no-msix stops once the web app is in place, which works on any system.

The package is signed with the certificate in MOONFIN_XBOX_PFX (password in MOONFIN_XBOX_PFX_PASSWORD), or with a throwaway one the script makes and keeps in .cert, which is all sideloading needs.

  1. Put the console in Developer Mode (the Xbox Dev Mode Activation app from the Store, with a Partner Center developer account) and turn on the Device Portal under Dev Home > Remote Access. Note the address it shows.
  2. Build, install and launch in one go:
cd packages/build-xbox
node build.js --debug --install <console ip>

--debug makes a Debug build whose version counts up on every install, so it goes on over the copy already there and the sign in survives, and it exposes the page to Edge's remote DevTools through the Device Portal. node console.js <console ip> install|launch|close|press|screenshot drives a console by itself. --tester makes the Release package to hand to testers, --store the upload for the Microsoft Store, and --dev-url <url> a Debug package that loads the app from npm run dev:xbox instead of the package, so changes show on the console as they are saved.

Development servers

npm run dev:tizen   # serves packages/app with REACT_APP_PLATFORM=tizen
npm run dev:webos   # serves packages/app with REACT_APP_PLATFORM=webos
npm run dev:vega    # serves packages/app with REACT_APP_PLATFORM=vega
npm run dev:xbox    # serves packages/app with REACT_APP_PLATFORM=xbox

Open the address it prints in a desktop browser. The platform is selected through REACT_APP_PLATFORM, so the dev server exercises the same platform detection the packaged app uses. See Development for how that abstraction works.

A browser dev server is subject to CORS restrictions that packaged .wgt and .ipk builds aren't, so some server requests behave differently there than on a TV. Focus behavior, performance and video don't fully reproduce on a desktop either. Test on real hardware before calling something done.

Checks before a pull request

cd packages/app
npx enact lint
npm test

The Tizen build also runs the legacy CSS gate (node scripts/check-legacy-css.js from the root), which fails on CSS the 2016 sets can't render.

Advanced: where the build CLIs live, and updating them

@enact/cli (both platforms) and @webos-tools/cli (the webOS ares-* packaging tools) are installed in the git-ignored tools/ directory rather than as devDependencies. Both publish npm-shrinkwrap.json files that pin their entire dependency trees, including outdated packages that npm cannot override or audit fix from a consuming repo, so keeping them out of the repo's lockfile keeps those pins out of the dependency graph and its Dependabot alerts. Their commands (enact, ares-package, ...) still work in npm scripts and via npx through thin forwarders in packages/cli-shim. The root postinstall (scripts/ensure-clis.js) installs them.

The versions are pinned in tools/package.json, currently @enact/cli 7.2.1 and @webos-tools/cli 3.2.5. To upgrade, bump the pin, delete tools/node_modules, run npm install at the root, then verify the three things a new @enact/cli can break: the lint gate (enact lint in packages/app), the legacy patches (node scripts/patch-enact-legacy.js must report no failures), and a full build of each platform. @enact/cli is held at 7.2.1 because 7.3.x ships eslint-plugin-react-hooks 6, whose new rules flag about 55 errors in the app and fail both the lint gate and enact pack. Upgrading it means fixing those findings first (the legacy patches already apply cleanly on 7.3.3).

Never add these CLIs back to devDependencies, and keep tools/package-lock.json git-ignored.

Advanced: signing the Samsung package with your own certificate

Only needed if you want the build script itself to produce a signed package. It requires the Tizen CLI, a Samsung certificate created with the Certificate Manager, and a profile in profiles.xml.

npm run build:tizen -- --signed

The script looks for a profile named Moonfin and for author.p12 and distributor.p12 under ~/SamsungCertificate/Moonfin (%USERPROFILE% on Windows). If your setup differs, point it at the right places:

Variable Purpose
TIZEN_SIGN_PROFILE Signing profile name (default Moonfin)
TIZEN_CERT_DIR Folder holding author.p12 and distributor.p12
TIZEN_PROFILES_XML Full path to your tooling's profiles.xml
TIZEN_CLI Full path to tizen / tizen.bat
TIZEN_STUDIO_HOME Tizen tooling install root, if it is somewhere unusual

Without all of certificate, profile and CLI present, the build falls back to an unsigned package and tells you what was missing. With valid certificates present, the plain npm run build:tizen signs by default. To force the unsigned path even when the Tizen CLI is installed:

npm run build:tizen -- --skip-tizen-cli

Missing .pwd files referenced by profiles.xml do not break signing.

Advanced: the Homebrew Channel manifest

npm run build:webos also refreshes packages/build-webos/org.moonfin.webos.manifest.json with the new version and the SHA-256 of the .ipk. That file is uploaded with every release and is what the webOS Homebrew Channel catalog reads, so it has to be committed alongside a version bump.

Troubleshooting

ClassNotFoundException: org.tizen.ncli.ide.shell.Main, or a path containing \\..\..\..\sdk.info

The Tizen CLI works out where it is installed from the path it was started with, and looks for sdk.info relative to that. If it is started by name rather than by full path, it searches from the wrong folder and cannot load its own libraries. The build script always calls it by full path, so first make sure you are building from current main. If it still happens, your Tizen tooling is in a location the build script does not know about, and setting TIZEN_CLI to the full path of tizen.bat will fix it. You can also skip it entirely with --skip-tizen-cli, since the CLI is only used for signing and for installing straight to a TV.

Samsung certificate or profiles.xml not found

These are warnings, not errors. The build continues and produces an unsigned package, which is what you want for normal sideloading with Apps2Samsung.

There is no <x> target from tizen install

You passed the ip:port from sdb devices. Pass the device name from the third column instead.

ares-install says the device is unreachable

Key Server is off in the Developer Mode app, or the passphrase changed after the TV restarted. Run npx ares-setup-device again with the current passphrase.

Build fails on Windows

Older versions shelled out to zip, which does not exist on Windows. Packaging is now done in Node with no external tools, so make sure you are building from current main.

npm install fails inside tools/

Delete tools/node_modules and run npm install at the root again. The CLIs are re-fetched from npm.

Clone this wiki locally