Skip to content

Commit 9e1b46d

Browse files
ralyodioclaude
andcommitted
feat(dist): Linux-phone packaging — arm64 .deb/.rpm + Ubuntu Touch click
Track 2 (docs/mobile-architecture.md): ship the noarch tron launcher (full Ungoogled Chromium + tor SOCKS5 parity) to Linux phones. - deb-rpm: arch-parameterized nfpm; build.sh emits amd64+arm64 by default. Add .desktop entry + hicolor icon (shows in Phosh/Plasma-Mobile app grid), Depends chromium|chromium-browser|ungoogled-chromium, Recommends tor. - ubuntu-touch/: Clickable 'pure' click package. Real engine runs in a Libertine container (tronbrowser-ut -> libertine-launch -> tron); unconfined AppArmor for sideload/OpenStore-unconfined. Staged + JSON-validated. - CI: new linux-phones.yml builds+verifies arm64 .deb and stages the click on push; release.yml now attaches arm64 debs/rpms and (non-fatally) the .click. - Docs: distribution/README matrix + architecture doc updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2fa5487 commit 9e1b46d

15 files changed

Lines changed: 352 additions & 28 deletions

File tree

‎.github/workflows/linux-phones.yml‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Linux phones
2+
3+
# Builds/validates the Linux-phone packaging (arm64 .deb/.rpm + Ubuntu Touch
4+
# click) on every relevant push — the release workflow attaches these to a
5+
# tagged release, this one just proves they build. See docs/mobile-architecture.md.
6+
on:
7+
push:
8+
branches: [main]
9+
paths:
10+
- 'distribution/deb-rpm/**'
11+
- 'distribution/ubuntu-touch/**'
12+
- 'apps/desktop/**'
13+
- '.github/workflows/linux-phones.yml'
14+
pull_request:
15+
paths:
16+
- 'distribution/deb-rpm/**'
17+
- 'distribution/ubuntu-touch/**'
18+
- 'apps/desktop/**'
19+
- '.github/workflows/linux-phones.yml'
20+
workflow_dispatch:
21+
22+
jobs:
23+
package:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v5
27+
28+
- name: Version
29+
id: v
30+
run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
31+
32+
- name: Install nfpm
33+
run: |
34+
curl -sSfL "https://github.com/goreleaser/nfpm/releases/download/v2.41.0/nfpm_2.41.0_amd64.deb" -o /tmp/nfpm.deb
35+
sudo dpkg -i /tmp/nfpm.deb
36+
37+
- name: Build noarch tarball
38+
run: bash apps/desktop/scripts/build-release.sh "v${{ steps.v.outputs.version }}" linux
39+
40+
- name: Build deb + rpm (amd64 + arm64)
41+
run: bash distribution/deb-rpm/build.sh "v${{ steps.v.outputs.version }}"
42+
43+
- name: Verify arm64 .deb (phones)
44+
run: |
45+
deb="dist/tronbrowser_${{ steps.v.outputs.version }}_arm64.deb"
46+
test -f "$deb" || { echo "missing $deb"; exit 1; }
47+
arch="$(dpkg-deb -f "$deb" Architecture)"
48+
[ "$arch" = "arm64" ] || { echo "bad arch: $arch"; exit 1; }
49+
dpkg-deb -c "$deb" | grep -q 'usr/share/applications/tronbrowser.desktop' \
50+
|| { echo "missing desktop entry"; exit 1; }
51+
echo "arm64 .deb OK: $deb"
52+
53+
- name: Stage + validate Ubuntu Touch click
54+
run: bash distribution/ubuntu-touch/build.sh "v${{ steps.v.outputs.version }}" arm64
55+
56+
- uses: actions/upload-artifact@v4
57+
with:
58+
name: linux-phone-packages
59+
path: |
60+
dist/*.deb
61+
dist/*.rpm
62+
dist/*.click
63+
if-no-files-found: warn

‎.github/workflows/release.yml‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,18 @@ jobs:
6767
curl -sSfL "https://github.com/goreleaser/nfpm/releases/download/v2.41.0/nfpm_2.41.0_amd64.deb" -o /tmp/nfpm.deb
6868
sudo dpkg -i /tmp/nfpm.deb
6969
sudo apt-get update -qq && sudo apt-get install -y -qq librsvg2-bin
70+
# deb + rpm for amd64 AND arm64 (arm64 = Librem 5 / PinePhone / postmarketOS)
7071
bash distribution/deb-rpm/build.sh "v${{ needs.create-release.outputs.version }}"
7172
bash distribution/appimage/build.sh "v${{ needs.create-release.outputs.version }}"
7273
74+
- name: Package (Ubuntu Touch click)
75+
if: matrix.platform == 'linux'
76+
continue-on-error: true # non-fatal: never block a release on the UT click
77+
run: |
78+
pip install --user clickable-ut || true
79+
export PATH="$HOME/.local/bin:$PATH"
80+
bash distribution/ubuntu-touch/build.sh "v${{ needs.create-release.outputs.version }}" arm64 || true
81+
7382
- name: Package (windows)
7483
if: matrix.platform == 'windows'
7584
shell: pwsh
@@ -90,7 +99,7 @@ jobs:
9099
GH_TOKEN: ${{ github.token }}
91100
run: |
92101
shopt -s nullglob
93-
assets=(dist/tronbrowser-*.tar.gz dist/tronbrowser-*.zip dist/*.deb dist/*.rpm dist/*.AppImage)
102+
assets=(dist/tronbrowser-*.tar.gz dist/tronbrowser-*.zip dist/*.deb dist/*.rpm dist/*.AppImage dist/*.click)
94103
gh release upload "${{ needs.create-release.outputs.tag }}" "${assets[@]}" --clobber
95104
96105
# 3) Publish (un-draft) once every platform has uploaded.

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ coverage/
2020

2121
# Bundled at build time (fetched, GPL — not vendored)
2222
apps/desktop/extensions/ublock-origin/
23+
24+
# Ubuntu Touch click staging (assembled from the release tarball)
25+
distribution/ubuntu-touch/install/
26+
distribution/ubuntu-touch/build/

‎apps/desktop/scripts/build-release.sh‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,28 @@ stage() { # dest dir
7474
cp -L "$REPO_ROOT/apps/web/public/icons/icon-512x512.png" "$s/tronbrowser.png" 2>/dev/null || true
7575
printf '%s\n' "$VERSION" > "$s/VERSION"
7676

77+
# Freedesktop .desktop entry so TronBrowser appears in app grids/menus —
78+
# notably the Linux-phone shells (Phosh on Librem 5, Phosh/Plasma Mobile on
79+
# PinePhone). The .deb installs this to /usr/share/applications; harmless in
80+
# the tarball. StartupWMClass matches the isolated-profile Chromium window.
81+
cat > "$s/tronbrowser.desktop" <<'DESKTOP'
82+
[Desktop Entry]
83+
Type=Application
84+
Name=TronBrowser
85+
GenericName=Web Browser
86+
Comment=Privacy-first, AI-native browser (Ungoogled Chromium)
87+
Exec=tron %U
88+
TryExec=tron
89+
Icon=tronbrowser
90+
Terminal=false
91+
Categories=Network;WebBrowser;
92+
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;
93+
Keywords=web;browser;privacy;tor;ai;
94+
StartupNotify=true
95+
StartupWMClass=tronbrowser
96+
X-Purism-FormFactor=Workstation;Mobile;
97+
DESKTOP
98+
7799
# NOTE: we no longer bundle NeverDecaf's chromium-web-store. Its manifest's
78100
# web_accessible_resources is rejected by some Chromium builds (macOS), which
79101
# aborts startup when loaded unpacked via --load-extension. Chrome Web Store

‎distribution/README.md‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ release assets. Goal: every major OS + open-source platform.
2323
| Universal Linux | Flatpak | [`flatpak/`](flatpak/) | 🟡 manifest ready (Flathub PR) |
2424
| Universal Linux | AppImage | [`appimage/`](appimage/) | ✅ built in CI |
2525
| FreeBSD | port | [`freebsd/`](freebsd/) | 🟡 port ready |
26+
| Librem 5 / PinePhone / postmarketOS | apt (arm64 .deb) | [`deb-rpm/`](deb-rpm/) (nfpm) | ✅ built in CI |
2627
| Librem 5 / PinePhone / Ubuntu Touch | `curl \| sh` (arm64) | install.sh | ✅ noarch launcher |
27-
| iOS / Android | Expo / EAS | [`apps/mobile`](../apps/mobile) | 🚧 Phase 2 |
28+
| Ubuntu Touch | click (Libertine) | [`ubuntu-touch/`](ubuntu-touch/) | 🟡 skeleton + CI-validated |
29+
| iOS / Android | Expo / EAS | [`apps/mobile`](../apps/mobile) | ✅ companion app |
2830

2931
## CI/CD (per platform, one repo)
3032

@@ -43,8 +45,10 @@ channel — gated on that channel's secret, dry-run by default.
4345
## Build a single channel locally
4446

4547
```bash
46-
bash apps/desktop/scripts/build-release.sh v0.1.1 linux # tar.gz
47-
bash distribution/deb-rpm/build.sh v0.1.1 # .deb + .rpm (needs nfpm)
48+
bash apps/desktop/scripts/build-release.sh v0.1.1 linux # tar.gz (noarch)
49+
bash distribution/deb-rpm/build.sh v0.1.1 # .deb + .rpm, amd64 + arm64 (needs nfpm)
50+
bash distribution/deb-rpm/build.sh v0.1.1 arm64 # arm64 only (phones)
51+
bash distribution/ubuntu-touch/build.sh v0.1.1 arm64 # Ubuntu Touch .click (needs Clickable)
4852
bash distribution/appimage/build.sh v0.1.1 # .AppImage
4953
node scripts/submit-packages.mjs -v 0.1.1 -p all --dry-run
5054
```

‎distribution/deb-rpm/build.sh‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
#!/usr/bin/env bash
22
# Build .deb and .rpm from the linux release tarball using nfpm.
3-
# Usage: distribution/deb-rpm/build.sh <version> (expects dist/tronbrowser-linux-x64.tar.gz)
3+
#
4+
# Usage: distribution/deb-rpm/build.sh <version> [arch ...]
5+
# arch defaults to "amd64 arm64" (arm64 is what Linux phones install).
6+
# Valid archs: amd64 arm64 (expects dist/tronbrowser-linux-x64.tar.gz)
7+
#
8+
# The staged tree is noarch, so every arch is packaged from the same tarball;
9+
# only the package's Architecture field (and deb/rpm arch tag) changes.
410
set -euo pipefail
511

6-
VERSION="${1:?usage: build.sh <version>}"
12+
VERSION="${1:?usage: build.sh <version> [arch ...]}"; shift || true
13+
ARCHES=("$@"); [ ${#ARCHES[@]} -gt 0 ] || ARCHES=(amd64 arm64)
14+
715
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
816
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
917
TARBALL="$ROOT/dist/tronbrowser-linux-x64.tar.gz"
@@ -16,12 +24,16 @@ tar -xzf "$TARBALL" -C "$work" # -> $work/tronbrowser
1624
TB_VERSION="${VERSION#v}"
1725
TB_ROOT="$work/tronbrowser"
1826

19-
rendered="$work/nfpm.yaml"
20-
sed -e "s|\${TB_VERSION}|${TB_VERSION}|g" -e "s|\${TB_ROOT}|${TB_ROOT}|g" \
21-
"$HERE/nfpm.yaml" > "$rendered"
27+
for TB_ARCH in "${ARCHES[@]}"; do
28+
rendered="$work/nfpm-${TB_ARCH}.yaml"
29+
sed -e "s|\${TB_VERSION}|${TB_VERSION}|g" \
30+
-e "s|\${TB_ROOT}|${TB_ROOT}|g" \
31+
-e "s|\${TB_ARCH}|${TB_ARCH}|g" \
32+
"$HERE/nfpm.yaml" > "$rendered"
33+
nfpm package -f "$rendered" -p deb -t "$ROOT/dist"
34+
nfpm package -f "$rendered" -p rpm -t "$ROOT/dist"
35+
done
2236

23-
nfpm package -f "$rendered" -p deb -t "$ROOT/dist"
24-
nfpm package -f "$rendered" -p rpm -t "$ROOT/dist"
2537
rm -rf "$work"
26-
echo "built deb + rpm:"
38+
echo "built deb + rpm (${ARCHES[*]}):"
2739
ls -lh "$ROOT/dist"/*.deb "$ROOT/dist"/*.rpm 2>/dev/null | awk '{print " "$9" ("$5")"}'

‎distribution/deb-rpm/nfpm.yaml‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
# nfpm config — builds .deb and .rpm from the staged TronBrowser tree.
2-
# Driven by packaging/build-deb-rpm.sh (sets TB_VERSION + TB_ROOT).
2+
# Driven by distribution/deb-rpm/build.sh (sets TB_VERSION, TB_ROOT, TB_ARCH).
3+
#
4+
# The packaged tree is noarch (POSIX launcher + extensions + SVG/PNG), so the
5+
# SAME staged tree produces amd64 AND arm64 packages — arm64 is what Linux
6+
# phones (Librem 5 / PureOS, PinePhone / Mobian, postmarketOS) install.
37
name: tronbrowser
4-
arch: amd64
8+
arch: ${TB_ARCH}
59
platform: linux
610
version: ${TB_VERSION}
711
section: web
@@ -11,8 +15,13 @@ description: Open-source, privacy-first, AI-native browser (Ungoogled Chromium f
1115
vendor: Profullstack, Inc.
1216
homepage: https://tronbrowser.dev
1317
license: MIT
18+
# Drives system Ungoogled Chromium / Chromium — accept whichever the distro
19+
# ships (phone distros vary: Mobian/PureOS ship `chromium`).
1420
depends:
15-
- chromium
21+
- chromium | chromium-browser | ungoogled-chromium
22+
# `tron --tor` needs the tor daemon; optional, so recommend (not depend).
23+
recommends:
24+
- tor
1625
contents:
1726
- src: ${TB_ROOT}/
1827
dst: /usr/lib/tronbrowser
@@ -22,3 +31,9 @@ contents:
2231
- src: /usr/lib/tronbrowser/tronbrowser
2332
dst: /usr/bin/tronbrowser
2433
type: symlink
34+
# Desktop entry + icon so TronBrowser shows in the phone app grid
35+
# (Phosh on Librem 5, Plasma Mobile / Phosh on PinePhone) and desktops.
36+
- src: ${TB_ROOT}/tronbrowser.desktop
37+
dst: /usr/share/applications/tronbrowser.desktop
38+
- src: ${TB_ROOT}/tronbrowser.png
39+
dst: /usr/share/icons/hicolor/512x512/apps/tronbrowser.png
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# TronBrowser on Ubuntu Touch (click)
2+
3+
Packages the noarch TronBrowser launcher as an Ubuntu Touch **click** app so it
4+
appears in the app grid and opens `http(s)` links.
5+
6+
## The honest constraint (read this first)
7+
8+
Ubuntu Touch **confines** click apps under AppArmor and ships the Morph/Oxide
9+
web engine — **not** a desktop Chromium. Running the *real* Ungoogled Chromium
10+
(the whole point of TronBrowser, incl. `--tor`) therefore needs a **Libertine**
11+
container, where a normal `chromium` is installed and driven by the bundled
12+
`tron` shim. This is the same full-parity engine as the desktop build — it just
13+
runs inside Libertine rather than under strict confinement.
14+
15+
Because Chromium-in-Libertine needs an `unconfined` AppArmor profile
16+
(`tronbrowser.apparmor`), this click is intended for **sideloading / the
17+
OpenStore "unconfined" track**, not the confined default. See
18+
[`../../docs/mobile-architecture.md`](../../docs/mobile-architecture.md) (Track 2).
19+
20+
## One-time device setup (Libertine)
21+
22+
In the Terminal app or over `adb shell`:
23+
24+
```bash
25+
libertine-container-manager create -i tron -n TronBrowser -t chroot
26+
libertine-container-manager install-package -i tron -p chromium
27+
```
28+
29+
The wrapper (`tronbrowser-ut`) then runs `libertine-launch -i tron tron <url>`.
30+
Override the container id with `TRONBROWSER_LIBERTINE_ID`.
31+
32+
## Build the click
33+
34+
```bash
35+
# 1) produce the noarch tarball (once)
36+
bash apps/desktop/scripts/build-release.sh v3.7.2 linux
37+
# 2) stage + build the click (arm64 default; also armhf/amd64)
38+
bash distribution/ubuntu-touch/build.sh v3.7.2 arm64
39+
```
40+
41+
`build.sh` stages `install/` and, if [Clickable](https://clickable-ut.dev) (or
42+
the raw `click` tool) is installed, emits a `.click` into `dist/`. Without a
43+
builder it leaves the validated `install/` tree.
44+
45+
## Install on device
46+
47+
```bash
48+
clickable install --arch arm64 # from this dir, device in dev mode
49+
# or push the .click and: pkcon install-local --allow-untrusted dist/*.click
50+
```
51+
52+
## Files
53+
54+
- `manifest.json` — click manifest (`@CLICK_ARCH@` + version filled by build.sh).
55+
- `tronbrowser.apparmor` — `unconfined` (required for Chromium-in-Libertine).
56+
- `tronbrowser.desktop` — app-grid entry (`Exec=tronbrowser-ut %u`).
57+
- `tronbrowser-ut` — entry wrapper: `libertine-launch` → bundled `tron`.
58+
- `clickable.yaml` — Clickable `pure` builder config.

‎distribution/ubuntu-touch/build.sh‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
# Assemble the TronBrowser Ubuntu Touch click package from the linux release
3+
# tarball (noarch launcher tree). Usage:
4+
# distribution/ubuntu-touch/build.sh <version> [arch]
5+
# arch: arm64 (default) | armhf | amd64 (expects dist/tronbrowser-linux-x64.tar.gz)
6+
#
7+
# Produces install/ (the click tree) and, if a click builder is available, a
8+
# .click under dist/. See README.md for the Libertine/confinement story.
9+
set -euo pipefail
10+
11+
VERSION="${1:?usage: build.sh <version> [arch]}"
12+
ARCH="${2:-arm64}"
13+
TB_VERSION="${VERSION#v}"
14+
15+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
16+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17+
TARBALL="$ROOT/dist/tronbrowser-linux-x64.tar.gz"
18+
[ -f "$TARBALL" ] || { echo "missing $TARBALL — run build-release.sh linux first" >&2; exit 1; }
19+
20+
INSTALL="$HERE/install"
21+
rm -rf "$INSTALL"; mkdir -p "$INSTALL/assets"
22+
23+
# Noarch launcher tree (the same one the .deb/tarball ship).
24+
tar -xzf "$TARBALL" -C "$INSTALL" # -> $INSTALL/tronbrowser
25+
install -m 0755 "$HERE/tronbrowser-ut" "$INSTALL/tronbrowser-ut"
26+
cp "$HERE/tronbrowser.apparmor" "$INSTALL/tronbrowser.apparmor"
27+
cp "$HERE/tronbrowser.desktop" "$INSTALL/tronbrowser.desktop"
28+
cp "$INSTALL/tronbrowser/tronbrowser.png" "$INSTALL/assets/tronbrowser.png"
29+
30+
# Render manifest (version + arch).
31+
sed -e "s|\"version\": \"[^\"]*\"|\"version\": \"${TB_VERSION}\"|" \
32+
-e "s|@CLICK_ARCH@|${ARCH}|" \
33+
"$HERE/manifest.json" > "$INSTALL/manifest.json"
34+
35+
echo "staged click tree at $INSTALL (arch=$ARCH, version=$TB_VERSION)"
36+
37+
# Validate the JSON we just produced.
38+
for j in manifest.json tronbrowser.apparmor; do
39+
python3 -c "import json,sys; json.load(open('$INSTALL/$j'))" \
40+
&& echo " ok: $j"
41+
done
42+
43+
# Build the .click if a builder is available; otherwise leave the staged tree.
44+
if command -v clickable >/dev/null 2>&1; then
45+
( cd "$HERE" && clickable build --arch "$ARCH" )
46+
elif command -v click >/dev/null 2>&1; then
47+
mkdir -p "$ROOT/dist"
48+
click build "$INSTALL" --output "$ROOT/dist"
49+
ls -lh "$ROOT/dist"/*.click 2>/dev/null | awk '{print " built "$9" ("$5")"}'
50+
else
51+
echo "note: no 'clickable' or 'click' found — staged tree only."
52+
echo " install Clickable (https://clickable-ut.dev) then: clickable build --arch $ARCH"
53+
fi
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Clickable config for the TronBrowser Ubuntu Touch click package.
2+
# https://clickable-ut.dev
3+
#
4+
# `pure` builder: no cross-compilation/containers — we ship the noarch launcher
5+
# tree, so the build just assembles files. `build.sh` stages the release tarball
6+
# into ./install before `clickable build` zips it into a .click.
7+
clickable_minimum_required: "8.0.0"
8+
builder: pure
9+
kill: tronbrowser-ut
10+
11+
# Default target arch for phones. Override with `clickable build --arch armhf`.
12+
arch: arm64
13+
14+
install_dir: install

0 commit comments

Comments
 (0)