mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(installer): prefer deb package on Debian Linux (#4263)
Co-authored-by: M3gA-Mind <megamind@mahadao.com>
This commit is contained in:
@@ -590,6 +590,9 @@ jobs:
|
|||||||
// Linux desktop installer consumed by scripts/install.sh and
|
// Linux desktop installer consumed by scripts/install.sh and
|
||||||
// advertised in latest.json as linux-x86_64.
|
// advertised in latest.json as linux-x86_64.
|
||||||
/OpenHuman_.*_amd64\.AppImage$/,
|
/OpenHuman_.*_amd64\.AppImage$/,
|
||||||
|
// Debian/Ubuntu script installs prefer release .deb assets so
|
||||||
|
// apt can resolve CEF runtime dependencies.
|
||||||
|
/OpenHuman_.*_amd64\.deb$/,
|
||||||
// Linux arm64 desktop installer consumed by scripts/install.sh
|
// Linux arm64 desktop installer consumed by scripts/install.sh
|
||||||
// and advertised in latest.json as linux-aarch64.
|
// and advertised in latest.json as linux-aarch64.
|
||||||
/OpenHuman_.*_(arm64|aarch64)\.AppImage$/,
|
/OpenHuman_.*_(arm64|aarch64)\.AppImage$/,
|
||||||
|
|||||||
@@ -62,11 +62,11 @@
|
|||||||
|
|
||||||
# Install
|
# Install
|
||||||
|
|
||||||
Download installers from [tinyhumans.ai/openhuman](https://tinyhumans.ai/openhuman?utm_source=github&utm_medium=readme) or from the [GitHub Releases](https://github.com/tinyhumansai/openhuman/releases/latest) page. For terminal installs, the native package paths below are preferred — they ride your OS package-manager's signing chain.
|
Download installers from [tinyhumans.ai/openhuman](https://tinyhumans.ai/openhuman?utm_source=github&utm_medium=readme) or from the [GitHub Releases](https://github.com/tinyhumansai/openhuman/releases/latest) page. For terminal installs, the native package paths below are preferred because they use your OS package manager or native installer where available.
|
||||||
|
|
||||||
## Recommended install (native packages)
|
## Recommended install (native packages)
|
||||||
|
|
||||||
These paths verify the artifact through your OS package manager's signing chain (Homebrew bottle hash, signed apt repo, MSI signature).
|
These paths use native installer surfaces. Homebrew and MSI provide their normal signing/integrity checks; Debian/Ubuntu uses `apt-get` to install the release `.deb` and resolve system dependencies.
|
||||||
|
|
||||||
**macOS (Homebrew tap):**
|
**macOS (Homebrew tap):**
|
||||||
|
|
||||||
@@ -75,17 +75,13 @@ brew tap tinyhumansai/core
|
|||||||
brew install openhuman
|
brew install openhuman
|
||||||
```
|
```
|
||||||
|
|
||||||
**Linux (Debian/Ubuntu — signed apt repo):**
|
**Linux (Debian/Ubuntu — release `.deb`):**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install -y --no-install-recommends gnupg2 curl ca-certificates
|
# Download OpenHuman_<version>_amd64.deb or OpenHuman_<version>_arm64.deb
|
||||||
curl -fsSL https://tinyhumansai.github.io/openhuman/apt/KEY.gpg \
|
# from https://github.com/tinyhumansai/openhuman/releases/latest, then:
|
||||||
| sudo gpg --dearmor -o /etc/apt/keyrings/openhuman.gpg
|
# Replace amd64 with arm64 on arm64 hosts.
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/openhuman.gpg arch=amd64] \
|
sudo apt-get install -y --no-install-recommends ./OpenHuman_*_amd64.deb
|
||||||
https://tinyhumansai.github.io/openhuman/apt stable main" \
|
|
||||||
| sudo tee /etc/apt/sources.list.d/openhuman.list
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y openhuman
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Linux (Arch — AUR):** the [`openhuman-bin` AUR recipe](./packages/arch/openhuman-bin/) is in the repo. Once published, Arch users can install it with `yay -S openhuman-bin`.
|
**Linux (Arch — AUR):** the [`openhuman-bin` AUR recipe](./packages/arch/openhuman-bin/) is in the repo. Once published, Arch users can install it with `yay -S openhuman-bin`.
|
||||||
@@ -94,7 +90,7 @@ sudo apt-get install -y openhuman
|
|||||||
|
|
||||||
**Manual `.dmg` / `.deb` / `.AppImage` / `.msi`:** grab the installer for your platform directly from the [latest release page](https://github.com/tinyhumansai/openhuman/releases/latest).
|
**Manual `.dmg` / `.deb` / `.AppImage` / `.msi`:** grab the installer for your platform directly from the [latest release page](https://github.com/tinyhumansai/openhuman/releases/latest).
|
||||||
|
|
||||||
> **Linux:** the AppImage can crash on launch under Wayland (and on Arch-based distros with `sharun: Interpreter not found!`) — see [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) for the cause and env-var workarounds. The `.deb` package above avoids those failure modes on Debian/Ubuntu.
|
> **Linux:** the AppImage can crash on launch under Wayland, miss host system libraries such as `libgbm.so.1`, or fail on Arch-based distros with `sharun: Interpreter not found!` — see [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) for the cause and env-var workarounds. The `.deb` package above avoids those failure modes on Debian/Ubuntu by letting apt resolve runtime dependencies.
|
||||||
|
|
||||||
## Alternative: script install (no integrity check)
|
## Alternative: script install (no integrity check)
|
||||||
|
|
||||||
@@ -108,6 +104,8 @@ curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts
|
|||||||
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex
|
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex
|
||||||
```
|
```
|
||||||
|
|
||||||
|
On Debian/Ubuntu, `install.sh` resolves the latest release `.deb` first and installs it with `apt-get` so runtime dependencies are handled by apt. Set `OPENHUMAN_INSTALLER_LINUX_PACKAGE=appimage` to force the AppImage path.
|
||||||
|
|
||||||
## Verified script install status
|
## Verified script install status
|
||||||
|
|
||||||
A separately signed script-install path is not currently available. Issue [#2620](https://github.com/tinyhumansai/openhuman/issues/2620) is closed after the native package paths were promoted, but current release assets do not include `install.sh.asc` / `install.ps1.asc` for pre-execution script verification. Treat the script install path as unverified and prefer the native package options above when possible.
|
A separately signed script-install path is not currently available. Issue [#2620](https://github.com/tinyhumansai/openhuman/issues/2620) is closed after the native package paths were promoted, but current release assets do not include `install.sh.asc` / `install.ps1.asc` for pre-execution script verification. Treat the script install path as unverified and prefer the native package options above when possible.
|
||||||
@@ -130,7 +128,7 @@ OpenHuman is an open-source agentic assistant designed to integrate with you in
|
|||||||
|
|
||||||
- **[Themes & Theme Studio](https://tinyhumans.gitbook.io/openhuman/features/theming)**: make it yours. Five built-in theme families (Classic, Ocean, Sepia, Matrix, HAL 9000) ship in light/dark/auto variants, and the **Theme Studio** in Settings is a full visual editor — adjust every colour token with live contrast warnings, swap fonts per role, pick an animated WebGL-mesh / flat / custom-image backdrop, and export or import themes as JSON to share. Editing a preset auto-forks a custom theme so the originals stay pristine; everything applies instantly and persists locally.
|
- **[Themes & Theme Studio](https://tinyhumans.gitbook.io/openhuman/features/theming)**: make it yours. Five built-in theme families (Classic, Ocean, Sepia, Matrix, HAL 9000) ship in light/dark/auto variants, and the **Theme Studio** in Settings is a full visual editor — adjust every colour token with live contrast warnings, swap fonts per role, pick an animated WebGL-mesh / flat / custom-image backdrop, and export or import themes as JSON to share. Editing a preset auto-forks a custom theme so the originals stay pristine; everything applies instantly and persists locally.
|
||||||
|
|
||||||
- **Batteries included**: web search, a web-fetch [scraper](https://tinyhumans.gitbook.io/openhuman/features/native-tools), a full coder toolset (filesystem, git, lint, test, grep), and [native voice](https://tinyhumans.gitbook.io/openhuman/features/voice) (STT in, ElevenLabs TTS out, mascot lip-sync, live Google Meet agent) are wired in by default. By default, [model routing](https://tinyhumans.gitbook.io/openhuman/features/model-routing) uses the OpenHuman backend to select and proxy the right LLM for each workload (reasoning, fast, or vision). One subscription includes all models. No "install a plugin to read files" friction. Use [optional local AI via Ollama](https://tinyhumans.gitbook.io/openhuman/features/model-routing/local-ai) for supported on-device workloads.
|
- **Batteries included**: web search, a web-fetch [scraper](https://tinyhumans.gitbook.io/openhuman/features/native-tools), a full coder toolset (filesystem, git, lint, test, grep), and [native voice](gitbooks/features/native-tools/voice.md) (STT in, ElevenLabs TTS out, mascot lip-sync, live Google Meet agent) are wired in by default. By default, [model routing](https://tinyhumans.gitbook.io/openhuman/features/model-routing) uses the OpenHuman backend to select and proxy the right LLM for each workload (reasoning, fast, or vision). One subscription includes all models. No "install a plugin to read files" friction. Use [optional local AI via Ollama](https://tinyhumans.gitbook.io/openhuman/features/model-routing/local-ai) for supported on-device workloads.
|
||||||
|
|
||||||
- **[Smart token compression (TokenJuice)](https://tinyhumans.gitbook.io/openhuman/features/token-compression)**: every tool call, scrape result, email body, and search payload is run through a token compression layer before it touches any LLM Model. HTML is converted to Markdown, long URLs are shortened, and verbose tool output is deduped and summarized via a configurable rule overlay etc... CJK, emoji, and other multi-byte text are preserved grapheme-by-grapheme — never stripped. You get the same information but at a fraction of the tokens. Reducing cost & latency by up to 80%.
|
- **[Smart token compression (TokenJuice)](https://tinyhumans.gitbook.io/openhuman/features/token-compression)**: every tool call, scrape result, email body, and search payload is run through a token compression layer before it touches any LLM Model. HTML is converted to Markdown, long URLs are shortened, and verbose tool output is deduped and summarized via a configurable rule overlay etc... CJK, emoji, and other multi-byte text are preserved grapheme-by-grapheme — never stripped. You get the same information but at a fraction of the tokens. Reducing cost & latency by up to 80%.
|
||||||
|
|
||||||
|
|||||||
@@ -53,12 +53,18 @@
|
|||||||
"linux": {
|
"linux": {
|
||||||
"deb": {
|
"deb": {
|
||||||
"depends": [
|
"depends": [
|
||||||
|
"libatk-bridge2.0-0",
|
||||||
|
"libgbm1",
|
||||||
|
"libgdk-pixbuf-2.0-0",
|
||||||
|
"libglib2.0-0",
|
||||||
"libgtk-3-0",
|
"libgtk-3-0",
|
||||||
|
"libnspr4",
|
||||||
|
"libnss3",
|
||||||
"libwebkit2gtk-4.1-0",
|
"libwebkit2gtk-4.1-0",
|
||||||
"libx11-6",
|
"libx11-6",
|
||||||
"libxdo3",
|
"libxdo3",
|
||||||
"libgdk-pixbuf-2.0-0",
|
"libxkbcommon0",
|
||||||
"libglib2.0-0"
|
"libxshmfence1"
|
||||||
],
|
],
|
||||||
"desktopTemplate": "main.desktop"
|
"desktopTemplate": "main.desktop"
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-13
@@ -66,11 +66,11 @@
|
|||||||
|
|
||||||
# انسٹال کریں
|
# انسٹال کریں
|
||||||
|
|
||||||
انسٹالر [tinyhumans.ai/openhuman](https://tinyhumans.ai/openhuman?utm_source=github&utm_medium=readme) سے یا [GitHub ریلیز](https://github.com/tinyhumansai/openhuman/releases/latest) صفحے سے ڈاؤن لوڈ کریں۔ ٹرمینل انسٹال کے لیے، نیچے دیے گئے مقامی پیکیج کے راستے ترجیحی ہیں — یہ آپ کے OS پیکیج مینیجر کے دستخطی سلسلے پر سوار ہوتے ہیں۔
|
انسٹالر [tinyhumans.ai/openhuman](https://tinyhumans.ai/openhuman?utm_source=github&utm_medium=readme) سے یا [GitHub ریلیز](https://github.com/tinyhumansai/openhuman/releases/latest) صفحے سے ڈاؤن لوڈ کریں۔ ٹرمینل انسٹال کے لیے، نیچے دیے گئے مقامی پیکیج کے راستے ترجیحی ہیں کیونکہ جہاں دستیاب ہو وہاں یہ OS پیکیج مینیجر یا مقامی انسٹالر استعمال کرتے ہیں۔
|
||||||
|
|
||||||
## تجویز کردہ انسٹال (مقامی پیکیجز)
|
## تجویز کردہ انسٹال (مقامی پیکیجز)
|
||||||
|
|
||||||
یہ راستے OS پیکیج مینیجر کے دستخطی سلسلے کے ذریعے آرٹیفیکٹ کی تصدیق کرتے ہیں (Homebrew bottle hash، signed apt repo، MSI signature)۔
|
یہ راستے مقامی installer surfaces استعمال کرتے ہیں۔ Homebrew اور MSI اپنی معمول کی signing/integrity checks فراہم کرتے ہیں؛ Debian/Ubuntu release `.deb` کو `apt-get` سے انسٹال کرتا ہے تاکہ سسٹم dependencies حل ہوں۔
|
||||||
|
|
||||||
**میک او ایس (ہومبریو ٹیپ):**
|
**میک او ایس (ہومبریو ٹیپ):**
|
||||||
|
|
||||||
@@ -87,21 +87,17 @@ brew install openhuman
|
|||||||
|
|
||||||
<div dir="rtl" lang="ur">
|
<div dir="rtl" lang="ur">
|
||||||
|
|
||||||
**لینکس (ڈیبین/ابونٹو — دستخط شدہ اے پی ٹی ریپو):**
|
**لینکس (ڈیبین/ابونٹو — ریلیز `.deb`):**
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div dir="ltr">
|
<div dir="ltr">
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install -y --no-install-recommends gnupg2 curl ca-certificates
|
# Download OpenHuman_<version>_amd64.deb or OpenHuman_<version>_arm64.deb
|
||||||
curl -fsSL https://tinyhumansai.github.io/openhuman/apt/KEY.gpg \
|
# from https://github.com/tinyhumansai/openhuman/releases/latest, then:
|
||||||
| sudo gpg --dearmor -o /etc/apt/keyrings/openhuman.gpg
|
# Replace amd64 with arm64 on arm64 hosts.
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/openhuman.gpg arch=amd64] \
|
sudo apt-get install -y --no-install-recommends ./OpenHuman_*_amd64.deb
|
||||||
https://tinyhumansai.github.io/openhuman/apt stable main" \
|
|
||||||
| sudo tee /etc/apt/sources.list.d/openhuman.list
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y openhuman
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -114,7 +110,7 @@ sudo apt-get install -y openhuman
|
|||||||
|
|
||||||
**دستی `.dmg` / `.deb` / `.AppImage` / `.msi`:** اپنے پلیٹ فارم کے لیے انسٹالر براہ راست [تازہ ترین ریلیز صفحے](https://github.com/tinyhumansai/openhuman/releases/latest) سے حاصل کریں۔
|
**دستی `.dmg` / `.deb` / `.AppImage` / `.msi`:** اپنے پلیٹ فارم کے لیے انسٹالر براہ راست [تازہ ترین ریلیز صفحے](https://github.com/tinyhumansai/openhuman/releases/latest) سے حاصل کریں۔
|
||||||
|
|
||||||
> **لینکس:** ایپ امیج وے لینڈ کے تحت لانچ ہوتے وقت کریش کر سکتا ہے (اور آرچ پر مبنی ڈسٹرو پر `sharun: Interpreter not found!` کے ساتھ) — وجہ اور env-var ورکاراؤنڈ کے لیے [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) دیکھیں۔ اوپر والا `.deb` پیکیج ڈیبین/ابونٹو پر ان ناکامیوں سے بچاتا ہے۔
|
> **لینکس:** ایپ امیج وے لینڈ کے تحت لانچ ہوتے وقت کریش کر سکتا ہے، `libgbm.so.1` جیسی میزبان سسٹم لائبریری کھو سکتا ہے، یا آرچ پر مبنی ڈسٹرو پر `sharun: Interpreter not found!` کے ساتھ ناکام ہو سکتا ہے — وجہ اور env-var ورکاراؤنڈ کے لیے [#2463](https://github.com/tinyhumansai/openhuman/issues/2463) دیکھیں۔ اوپر والا `.deb` پیکیج ڈیبین/ابونٹو پر apt کے ذریعے runtime dependencies حل کرتا ہے۔
|
||||||
|
|
||||||
## متبادل: اسکرپٹ انسٹال (مربوطیت کی تصدیق نہیں)
|
## متبادل: اسکرپٹ انسٹال (مربوطیت کی تصدیق نہیں)
|
||||||
|
|
||||||
@@ -136,6 +132,8 @@ irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/instal
|
|||||||
|
|
||||||
<div dir="rtl" lang="ur">
|
<div dir="rtl" lang="ur">
|
||||||
|
|
||||||
|
ڈیبین/ابونٹو پر `install.sh` پہلے تازہ ترین release `.deb` resolve کرتا ہے اور اسے `apt-get` سے انسٹال کرتا ہے تاکہ runtime dependencies apt حل کرے۔ AppImage راستہ زبردستی استعمال کرنے کے لیے `OPENHUMAN_INSTALLER_LINUX_PACKAGE=appimage` سیٹ کریں۔
|
||||||
|
|
||||||
## تصدیق شدہ اسکرپٹ انسٹال کی حیثیت
|
## تصدیق شدہ اسکرپٹ انسٹال کی حیثیت
|
||||||
|
|
||||||
الگ سے دستخط شدہ اسکرپٹ انسٹال کا راستہ فی الحال دستیاب نہیں ہے۔ مقامی پیکیج کے راستوں کو فروغ دینے کے بعد [#2620](https://github.com/tinyhumansai/openhuman/issues/2620) بند کر دیا گیا ہے، لیکن موجودہ ریلیز اثاثوں میں پہلے سے عملدرآمد کی اسکرپٹ تصدیق کے لیے `install.sh.asc` / `install.ps1.asc` شامل نہیں ہیں۔ اسکرپٹ انسٹال کے راستے کو غیر تصدیق شدہ سمجھیں اور جب ممکن ہو اوپر والے مقامی پیکیج کے اختیارات کو ترجیح دیں۔
|
الگ سے دستخط شدہ اسکرپٹ انسٹال کا راستہ فی الحال دستیاب نہیں ہے۔ مقامی پیکیج کے راستوں کو فروغ دینے کے بعد [#2620](https://github.com/tinyhumansai/openhuman/issues/2620) بند کر دیا گیا ہے، لیکن موجودہ ریلیز اثاثوں میں پہلے سے عملدرآمد کی اسکرپٹ تصدیق کے لیے `install.sh.asc` / `install.ps1.asc` شامل نہیں ہیں۔ اسکرپٹ انسٹال کے راستے کو غیر تصدیق شدہ سمجھیں اور جب ممکن ہو اوپر والے مقامی پیکیج کے اختیارات کو ترجیح دیں۔
|
||||||
@@ -225,7 +223,7 @@ _AGI اور مصنوعی شعور کی طرف بڑھ رہے ہیں؟ ریپو ک
|
|||||||
<div dir="ltr">
|
<div dir="ltr">
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://www.star-history.com">
|
<a href="https://www.star-history.com/#tinyhumansai/openhuman&type=date&legend=top-left">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tinyhumansai/openhuman&type=date&theme=dark&legend=top-left" />
|
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tinyhumansai/openhuman&type=date&theme=dark&legend=top-left" />
|
||||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tinyhumansai/openhuman&type=date&legend=top-left" />
|
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tinyhumansai/openhuman&type=date&legend=top-left" />
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ Applies to every release, all platforms.
|
|||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
- [ ] **`.deb` and/or `.AppImage` install on a clean Ubuntu 22.04** — `sudo dpkg -i openhuman_*.deb` or `chmod +x openhuman-*.AppImage && ./openhuman-*.AppImage`. Expected: no missing-dependency errors; app launches.
|
- [ ] **Public `install.sh` prefers the `.deb` path on clean Ubuntu 24.04** — Run `curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh | bash` on a host with `apt-get` and `dpkg`. Expected: the script resolves `OpenHuman_*_amd64.deb` or `OpenHuman_*_arm64.deb`, installs it with `apt-get`, and launch does not fail on missing CEF runtime libraries such as `libgbm.so.1`.
|
||||||
|
- [ ] **`.deb` and/or `.AppImage` install on a clean Ubuntu 22.04** — `sudo apt-get install -y --no-install-recommends ./OpenHuman_*.deb` or `chmod +x OpenHuman_*.AppImage && ./OpenHuman_*.AppImage`. Expected: no missing-dependency errors; app launches.
|
||||||
- [ ] **`.AppImage` launches on a clean Ubuntu 24.04 host without a sibling extracted tree** — Run the downloaded AppImage directly from an empty directory. Expected: no `Interpreter not found!` error; `sharun` finds its bundled dynamic linker and the app reaches the first window.
|
- [ ] **`.AppImage` launches on a clean Ubuntu 24.04 host without a sibling extracted tree** — Run the downloaded AppImage directly from an empty directory. Expected: no `Interpreter not found!` error; `sharun` finds its bundled dynamic linker and the app reaches the first window.
|
||||||
- [ ] **OS-native notification toasts fire** — Trigger a notification from inside the app (e.g. memory captured, agent finished). Expected: a libnotify-style toast appears outside the app window. (CI Linux sees only Xvfb; this surface verifies on a real desktop.)
|
- [ ] **OS-native notification toasts fire** — Trigger a notification from inside the app (e.g. memory captured, agent finished). Expected: a libnotify-style toast appears outside the app window. (CI Linux sees only Xvfb; this surface verifies on a real desktop.)
|
||||||
- [ ] **Headless supervisor update stages without self-exit** — On a Linux service deployment with `[update] restart_strategy = "supervisor"` and `rpc_mutations_enabled = false`, stage a new core binary through the documented operator flow. Expected: the running process stays up until the supervisor restart, the staged binary is present on disk, and `systemctl restart openhuman` (or equivalent) picks up the new version.
|
- [ ] **Headless supervisor update stages without self-exit** — On a Linux service deployment with `[update] restart_strategy = "supervisor"` and `rpc_mutations_enabled = false`, stage a new core binary through the documented operator flow. Expected: the running process stays up until the supervisor restart, the staged binary is present on disk, and `systemctl restart openhuman` (or equivalent) picks up the new version.
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"tag_name": "v0.0.0-test",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"name": "OpenHuman_0.0.0-test_amd64.AppImage",
|
||||||
|
"browser_download_url": "https://example.invalid/OpenHuman_0.0.0-test_amd64.AppImage",
|
||||||
|
"digest": "sha256:appimage-amd64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenHuman_0.0.0-test_aarch64.AppImage",
|
||||||
|
"browser_download_url": "https://example.invalid/OpenHuman_0.0.0-test_aarch64.AppImage",
|
||||||
|
"digest": "sha256:appimage-aarch64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenHuman_0.0.0-test_amd64.deb",
|
||||||
|
"browser_download_url": "https://example.invalid/OpenHuman_0.0.0-test_amd64.deb",
|
||||||
|
"digest": "sha256:deb-amd64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenHuman_0.0.0-test_arm64.deb",
|
||||||
|
"browser_download_url": "https://example.invalid/OpenHuman_0.0.0-test_arm64.deb",
|
||||||
|
"digest": "sha256:deb-arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenHuman_0.0.0-test_aarch64-apple-darwin.app.tar.gz",
|
||||||
|
"browser_download_url": "https://example.invalid/OpenHuman_0.0.0-test_aarch64-apple-darwin.app.tar.gz",
|
||||||
|
"digest": "sha256:darwin-aarch64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+256
-144
@@ -205,6 +205,90 @@ PY
|
|||||||
printf '%s\n' "$url"
|
printf '%s\n' "$url"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linux_package_preference() {
|
||||||
|
if [ "${OS:-}" != "linux" ]; then
|
||||||
|
printf 'appimage\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${OPENHUMAN_INSTALLER_LINUX_PACKAGE:-auto}" in
|
||||||
|
deb)
|
||||||
|
printf 'deb\n'
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
appimage)
|
||||||
|
printf 'appimage\n'
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
auto|"")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf 'appimage\n'
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if command -v apt-get >/dev/null 2>&1 && command -v dpkg >/dev/null 2>&1; then
|
||||||
|
printf 'deb\n'
|
||||||
|
else
|
||||||
|
printf 'appimage\n'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_release_asset_metadata() {
|
||||||
|
local json_path="$1" os_name="$2" arch="$3" linux_asset_kind="${4:-appimage}"
|
||||||
|
python3 - "$json_path" "$os_name" "$arch" "$linux_asset_kind" <<'PY'
|
||||||
|
import json, re, sys
|
||||||
|
path, os_name, arch, linux_asset_kind = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
tag = (data.get("tag_name") or "").lstrip("v")
|
||||||
|
assets = data.get("assets", [])
|
||||||
|
|
||||||
|
def first_matching(names, pattern):
|
||||||
|
for name in names:
|
||||||
|
if re.search(pattern, name):
|
||||||
|
return name
|
||||||
|
return None
|
||||||
|
|
||||||
|
def choose_asset():
|
||||||
|
names = [a.get("name", "") for a in assets]
|
||||||
|
chosen = None
|
||||||
|
if os_name == "darwin" and arch == "aarch64":
|
||||||
|
chosen = first_matching(names, r"aarch64.*\.app\.tar\.gz$")
|
||||||
|
if not chosen:
|
||||||
|
chosen = first_matching(names, r"aarch64\.dmg$")
|
||||||
|
elif os_name == "darwin" and arch == "x86_64":
|
||||||
|
chosen = first_matching(names, r"(x86_64-apple-darwin|x64).*\.app\.tar\.gz$")
|
||||||
|
if not chosen:
|
||||||
|
chosen = first_matching(names, r"x64\.dmg$")
|
||||||
|
elif os_name == "linux" and arch == "x86_64":
|
||||||
|
if linux_asset_kind == "deb":
|
||||||
|
chosen = first_matching(names, r"OpenHuman_.*_amd64\.deb$")
|
||||||
|
else:
|
||||||
|
chosen = first_matching(names, r"amd64\.AppImage$")
|
||||||
|
elif os_name == "linux" and arch == "aarch64":
|
||||||
|
if linux_asset_kind == "deb":
|
||||||
|
chosen = first_matching(names, r"OpenHuman_.*_(arm64|aarch64)\.deb$")
|
||||||
|
else:
|
||||||
|
chosen = first_matching(names, r"(arm64|aarch64)\.AppImage$")
|
||||||
|
if not chosen:
|
||||||
|
return "", "", ""
|
||||||
|
for asset in assets:
|
||||||
|
if asset.get("name") == chosen:
|
||||||
|
return chosen, asset.get("browser_download_url", ""), (asset.get("digest", "") or "").replace("sha256:", "")
|
||||||
|
return "", "", ""
|
||||||
|
|
||||||
|
name, url, digest = choose_asset()
|
||||||
|
if not name or not url:
|
||||||
|
sys.exit(2)
|
||||||
|
print(tag)
|
||||||
|
print(name)
|
||||||
|
print(url)
|
||||||
|
print(digest)
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
# curl can fail on GitHub/CDN HTTP/2 framing issues on some networks while the
|
# curl can fail on GitHub/CDN HTTP/2 framing issues on some networks while the
|
||||||
# same URL succeeds over HTTP/1.1. Try the normal path first, then a
|
# same URL succeeds over HTTP/1.1. Try the normal path first, then a
|
||||||
# compatibility retry before surfacing the failure.
|
# compatibility retry before surfacing the failure.
|
||||||
@@ -302,62 +386,14 @@ resolve_from_release_api() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local parsed
|
local parsed linux_kind rc
|
||||||
parsed="$(python3 - "${RELEASE_JSON_PATH}" "${OS}" "${ARCH}" <<'PY'
|
linux_kind="${LINUX_PACKAGE_KIND:-$(linux_package_preference)}"
|
||||||
import json, re, sys
|
if parsed="$(resolve_release_asset_metadata "${RELEASE_JSON_PATH}" "${OS}" "${ARCH}" "${linux_kind}")"; then
|
||||||
path, os_name, arch = sys.argv[1], sys.argv[2], sys.argv[3]
|
rc=0
|
||||||
with open(path, "r", encoding="utf-8") as f:
|
else
|
||||||
data = json.load(f)
|
rc=$?
|
||||||
tag = (data.get("tag_name") or "").lstrip("v")
|
return "${rc}"
|
||||||
assets = data.get("assets", [])
|
fi
|
||||||
|
|
||||||
def choose_asset():
|
|
||||||
names = [a.get("name", "") for a in assets]
|
|
||||||
chosen = None
|
|
||||||
if os_name == "darwin" and arch == "aarch64":
|
|
||||||
for n in names:
|
|
||||||
if re.search(r"aarch64.*\.app\.tar\.gz$", n):
|
|
||||||
chosen = n
|
|
||||||
break
|
|
||||||
if not chosen:
|
|
||||||
for n in names:
|
|
||||||
if re.search(r"aarch64\.dmg$", n):
|
|
||||||
chosen = n
|
|
||||||
break
|
|
||||||
elif os_name == "darwin" and arch == "x86_64":
|
|
||||||
for n in names:
|
|
||||||
if re.search(r"(x86_64-apple-darwin|x64).*\.app\.tar\.gz$", n):
|
|
||||||
chosen = n
|
|
||||||
break
|
|
||||||
if not chosen:
|
|
||||||
for n in names:
|
|
||||||
if re.search(r"x64\.dmg$", n):
|
|
||||||
chosen = n
|
|
||||||
break
|
|
||||||
elif os_name == "linux" and arch == "x86_64":
|
|
||||||
for n in names:
|
|
||||||
if re.search(r"amd64\.AppImage$", n):
|
|
||||||
chosen = n
|
|
||||||
break
|
|
||||||
elif os_name == "linux" and arch == "aarch64":
|
|
||||||
for n in names:
|
|
||||||
if re.search(r"(arm64|aarch64)\.AppImage$", n):
|
|
||||||
chosen = n
|
|
||||||
break
|
|
||||||
if not chosen:
|
|
||||||
return "", "", ""
|
|
||||||
for asset in assets:
|
|
||||||
if asset.get("name") == chosen:
|
|
||||||
return chosen, asset.get("browser_download_url", ""), (asset.get("digest", "") or "").replace("sha256:", "")
|
|
||||||
return "", "", ""
|
|
||||||
|
|
||||||
name, url, digest = choose_asset()
|
|
||||||
print(tag)
|
|
||||||
print(name)
|
|
||||||
print(url)
|
|
||||||
print(digest)
|
|
||||||
PY
|
|
||||||
)" || return 1
|
|
||||||
|
|
||||||
if [ -z "${LATEST_VERSION}" ]; then
|
if [ -z "${LATEST_VERSION}" ]; then
|
||||||
LATEST_VERSION="$(echo "${parsed}" | sed -n '1p')"
|
LATEST_VERSION="$(echo "${parsed}" | sed -n '1p')"
|
||||||
@@ -404,70 +440,6 @@ PY
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ "${SOURCE_ONLY}" == "1" ]]; then
|
|
||||||
return 0 2>/dev/null || exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if resolve_from_latest_json; then
|
|
||||||
log_ok "Resolved latest release via latest.json (${LATEST_VERSION})"
|
|
||||||
else
|
|
||||||
log_warn "latest.json lookup failed. Falling back to releases API."
|
|
||||||
# Wrap the call so `set -e` can't abort before rc is captured. Without the
|
|
||||||
# `if`-guard, `resolve_from_release_api` returning a non-zero rc (e.g. 2 for
|
|
||||||
# "no compatible asset") trips `set -euo pipefail` and exits the script
|
|
||||||
# before the handler below can decide dry-run vs real-install behavior.
|
|
||||||
if resolve_from_release_api; then
|
|
||||||
resolve_rc=0
|
|
||||||
else
|
|
||||||
resolve_rc=$?
|
|
||||||
fi
|
|
||||||
if [ "${resolve_rc}" -ne 0 ]; then
|
|
||||||
# Dry-run is a "what would happen?" query, not an install. If the release
|
|
||||||
# metadata says no compatible asset exists (or the metadata itself can't
|
|
||||||
# be reached), surface a warning and exit 0 so installer smoke checks on
|
|
||||||
# platforms without a current build don't fail the whole CI matrix. Real
|
|
||||||
# installs (non-dry-run) still hard-fail below.
|
|
||||||
if [ "${DRY_RUN}" = true ]; then
|
|
||||||
case "${resolve_rc}" in
|
|
||||||
2)
|
|
||||||
log_warn "No compatible release asset published yet for ${OS}/${ARCH}."
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
log_warn "Could not reach release metadata (rc=${resolve_rc}) for ${OS}/${ARCH}."
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
echo "DRY RUN: skipping install for ${OS}/${ARCH} — no asset resolved."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
log_err "Could not resolve a compatible asset for ${OS}/${ARCH}."
|
|
||||||
log_err "Check https://github.com/${REPO}/releases/latest for available assets."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
log_ok "Resolved latest release via releases API (${LATEST_VERSION})"
|
|
||||||
fi
|
|
||||||
|
|
||||||
resolve_release_digest
|
|
||||||
|
|
||||||
if [ -z "${ASSET_URL}" ]; then
|
|
||||||
log_err "Could not determine download URL for ${OS}/${ARCH}."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${DRY_RUN}" = true ]; then
|
|
||||||
echo "DRY RUN: verify asset reachable ${ASSET_URL}"
|
|
||||||
elif ! verify_asset_reachable "${ASSET_URL}"; then
|
|
||||||
log_err "Asset URL is not reachable for ${OS}/${ARCH}: ${ASSET_URL}"
|
|
||||||
exit 4
|
|
||||||
fi
|
|
||||||
|
|
||||||
DOWNLOAD_PATH="${TMP_DIR}/${ASSET_NAME}"
|
|
||||||
log_info "Downloading ${ASSET_NAME}"
|
|
||||||
if [ "${DRY_RUN}" = true ]; then
|
|
||||||
echo "DRY RUN: curl -fL ${ASSET_URL} -o ${DOWNLOAD_PATH} (retrying with --http1.1 on failure)"
|
|
||||||
else
|
|
||||||
curl_download_file "${ASSET_URL}" "${DOWNLOAD_PATH}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
compute_sha256() {
|
compute_sha256() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
if command -v sha256sum >/dev/null 2>&1; then
|
if command -v sha256sum >/dev/null 2>&1; then
|
||||||
@@ -481,26 +453,6 @@ compute_sha256() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ -n "${ASSET_SHA256}" ]; then
|
|
||||||
if [ "${DRY_RUN}" = true ]; then
|
|
||||||
echo "DRY RUN: verify sha256 ${ASSET_SHA256} for ${DOWNLOAD_PATH}"
|
|
||||||
else
|
|
||||||
actual_sha256="$(compute_sha256 "${DOWNLOAD_PATH}" || true)"
|
|
||||||
if [ -z "${actual_sha256}" ]; then
|
|
||||||
log_warn "No checksum command available; skipping digest verification."
|
|
||||||
elif [ "${actual_sha256}" != "${ASSET_SHA256}" ]; then
|
|
||||||
log_err "SHA256 mismatch for ${ASSET_NAME}"
|
|
||||||
log_err "Expected: ${ASSET_SHA256}"
|
|
||||||
log_err "Actual: ${actual_sha256}"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
log_ok "Integrity verified (sha256)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log_warn "No SHA256 digest available for ${ASSET_NAME}; skipping integrity verification."
|
|
||||||
fi
|
|
||||||
|
|
||||||
ensure_local_bin_path() {
|
ensure_local_bin_path() {
|
||||||
local bin_dir="${HOME}/.local/bin"
|
local bin_dir="${HOME}/.local/bin"
|
||||||
if echo ":${PATH}:" | grep -q ":${bin_dir}:"; then
|
if echo ":${PATH}:" | grep -q ":${bin_dir}:"; then
|
||||||
@@ -532,6 +484,38 @@ ensure_local_bin_path() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apt_get_command_prefix() {
|
||||||
|
if [ "${EUID:-$(id -u)}" -eq 0 ]; then
|
||||||
|
printf 'apt-get'
|
||||||
|
else
|
||||||
|
printf 'sudo apt-get'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_linux_deb() {
|
||||||
|
if [ "${DRY_RUN}" = true ]; then
|
||||||
|
echo "DRY RUN: $(apt_get_command_prefix) install -y --no-install-recommends ${DOWNLOAD_PATH}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v apt-get >/dev/null 2>&1; then
|
||||||
|
log_err "apt-get is required to install ${ASSET_NAME} and resolve dependencies."
|
||||||
|
log_err "Set OPENHUMAN_INSTALLER_LINUX_PACKAGE=appimage to force the AppImage path."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${EUID:-$(id -u)}" -eq 0 ]; then
|
||||||
|
apt-get install -y --no-install-recommends "${DOWNLOAD_PATH}"
|
||||||
|
else
|
||||||
|
if ! command -v sudo >/dev/null 2>&1; then
|
||||||
|
log_err "sudo is required to install ${ASSET_NAME} with apt-get."
|
||||||
|
log_err "Re-run as root or install the .deb manually with apt-get."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sudo apt-get install -y --no-install-recommends "${DOWNLOAD_PATH}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
install_macos() {
|
install_macos() {
|
||||||
local apps_dir="${HOME}/Applications"
|
local apps_dir="${HOME}/Applications"
|
||||||
local app_path="${apps_dir}/OpenHuman.app"
|
local app_path="${apps_dir}/OpenHuman.app"
|
||||||
@@ -590,10 +574,21 @@ install_linux() {
|
|||||||
local desktop_dir="${HOME}/.local/share/applications"
|
local desktop_dir="${HOME}/.local/share/applications"
|
||||||
local desktop_file="${desktop_dir}/openhuman.desktop"
|
local desktop_file="${desktop_dir}/openhuman.desktop"
|
||||||
|
|
||||||
|
if [[ "${ASSET_NAME}" =~ \.deb$ ]]; then
|
||||||
|
log_info "Installing Debian package with apt-get so system dependencies resolve"
|
||||||
|
install_linux_deb
|
||||||
|
log_ok "Installed Debian package ${ASSET_NAME}"
|
||||||
|
echo ""
|
||||||
|
echo "OpenHuman is ready."
|
||||||
|
echo "Launch: openhuman"
|
||||||
|
echo "Uninstall: $(apt_get_command_prefix) remove openhuman"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p "${bin_dir}" "${desktop_dir}"
|
mkdir -p "${bin_dir}" "${desktop_dir}"
|
||||||
|
|
||||||
if [[ ! "${ASSET_NAME}" =~ \.AppImage$ ]]; then
|
if [[ ! "${ASSET_NAME}" =~ \.AppImage$ ]]; then
|
||||||
log_err "Expected AppImage for Linux install, got: ${ASSET_NAME}"
|
log_err "Expected AppImage or .deb for Linux install, got: ${ASSET_NAME}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -626,13 +621,130 @@ EOF
|
|||||||
echo ""
|
echo ""
|
||||||
echo "OpenHuman is ready."
|
echo "OpenHuman is ready."
|
||||||
echo "Launch: ${app_path}"
|
echo "Launch: ${app_path}"
|
||||||
echo "If the AppImage prints 'Interpreter not found!' or unshare/uid_map errors,"
|
echo "If the AppImage prints 'Interpreter not found!', unshare/uid_map errors,"
|
||||||
echo "try the .deb package from https://github.com/${REPO}/releases/latest"
|
echo "or missing system-library errors such as libgbm.so.1, use the .deb package"
|
||||||
echo "(Debian/Ubuntu) or report at https://github.com/${REPO}/issues with your"
|
echo "from https://github.com/${REPO}/releases/latest on Debian/Ubuntu."
|
||||||
|
echo "For other distros, report at https://github.com/${REPO}/issues with your"
|
||||||
echo "distro, kernel (uname -a), GPU driver (lspci), dmesg excerpt, and asset: ${ASSET_NAME}."
|
echo "distro, kernel (uname -a), GPU driver (lspci), dmesg excerpt, and asset: ${ASSET_NAME}."
|
||||||
echo "Uninstall: rm -f \"${app_path}\" \"${desktop_file}\""
|
echo "Uninstall: rm -f \"${app_path}\" \"${desktop_file}\""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if [[ "${SOURCE_ONLY}" == "1" ]]; then
|
||||||
|
return 0 2>/dev/null || exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
resolve_rc=0
|
||||||
|
linux_package_request="${OPENHUMAN_INSTALLER_LINUX_PACKAGE:-auto}"
|
||||||
|
|
||||||
|
if [ "${OS}" = "linux" ] && [ "$(linux_package_preference)" = "deb" ]; then
|
||||||
|
LINUX_PACKAGE_KIND="deb"
|
||||||
|
log_info "Debian/Ubuntu package tooling detected; resolving .deb release asset."
|
||||||
|
if resolve_from_release_api; then
|
||||||
|
log_ok "Resolved latest release via releases API (${LATEST_VERSION})"
|
||||||
|
else
|
||||||
|
resolve_rc=$?
|
||||||
|
if [ "${linux_package_request}" = "deb" ]; then
|
||||||
|
if [ "${DRY_RUN}" = true ]; then
|
||||||
|
log_warn "Could not resolve requested .deb release asset (rc=${resolve_rc}) for ${OS}/${ARCH}."
|
||||||
|
echo "DRY RUN: skipping install for ${OS}/${ARCH} - no .deb asset resolved."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
log_err "Could not resolve requested .deb release asset for ${OS}/${ARCH}."
|
||||||
|
log_err "Set OPENHUMAN_INSTALLER_LINUX_PACKAGE=appimage to use the AppImage fallback."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
case "${resolve_rc}" in
|
||||||
|
2)
|
||||||
|
log_warn "No .deb release asset published yet for ${OS}/${ARCH}; falling back to AppImage metadata."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_warn "Could not resolve .deb via releases API (rc=${resolve_rc}); falling back to AppImage metadata."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
LINUX_PACKAGE_KIND="appimage"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${ASSET_URL}" ] && resolve_from_latest_json; then
|
||||||
|
log_ok "Resolved latest release via latest.json (${LATEST_VERSION})"
|
||||||
|
elif [ -z "${ASSET_URL}" ]; then
|
||||||
|
log_warn "latest.json lookup failed. Falling back to releases API."
|
||||||
|
# Wrap the call so `set -e` can't abort before rc is captured. Without the
|
||||||
|
# `if`-guard, `resolve_from_release_api` returning a non-zero rc (e.g. 2 for
|
||||||
|
# "no compatible asset") trips `set -euo pipefail` and exits the script
|
||||||
|
# before the handler below can decide dry-run vs real-install behavior.
|
||||||
|
if resolve_from_release_api; then
|
||||||
|
resolve_rc=0
|
||||||
|
else
|
||||||
|
resolve_rc=$?
|
||||||
|
fi
|
||||||
|
if [ "${resolve_rc}" -ne 0 ]; then
|
||||||
|
# Dry-run is a "what would happen?" query, not an install. If the release
|
||||||
|
# metadata says no compatible asset exists (or the metadata itself can't
|
||||||
|
# be reached), surface a warning and exit 0 so installer smoke checks on
|
||||||
|
# platforms without a current build don't fail the whole CI matrix. Real
|
||||||
|
# installs (non-dry-run) still hard-fail below.
|
||||||
|
if [ "${DRY_RUN}" = true ]; then
|
||||||
|
case "${resolve_rc}" in
|
||||||
|
2)
|
||||||
|
log_warn "No compatible release asset published yet for ${OS}/${ARCH}."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_warn "Could not reach release metadata (rc=${resolve_rc}) for ${OS}/${ARCH}."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo "DRY RUN: skipping install for ${OS}/${ARCH} — no asset resolved."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
log_err "Could not resolve a compatible asset for ${OS}/${ARCH}."
|
||||||
|
log_err "Check https://github.com/${REPO}/releases/latest for available assets."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_ok "Resolved latest release via releases API (${LATEST_VERSION})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
resolve_release_digest
|
||||||
|
|
||||||
|
if [ -z "${ASSET_URL}" ]; then
|
||||||
|
log_err "Could not determine download URL for ${OS}/${ARCH}."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${DRY_RUN}" = true ]; then
|
||||||
|
echo "DRY RUN: verify asset reachable ${ASSET_URL}"
|
||||||
|
elif ! verify_asset_reachable "${ASSET_URL}"; then
|
||||||
|
log_err "Asset URL is not reachable for ${OS}/${ARCH}: ${ASSET_URL}"
|
||||||
|
exit 4
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOWNLOAD_PATH="${TMP_DIR}/${ASSET_NAME}"
|
||||||
|
log_info "Downloading ${ASSET_NAME}"
|
||||||
|
if [ "${DRY_RUN}" = true ]; then
|
||||||
|
echo "DRY RUN: curl -fL ${ASSET_URL} -o ${DOWNLOAD_PATH} (retrying with --http1.1 on failure)"
|
||||||
|
else
|
||||||
|
curl_download_file "${ASSET_URL}" "${DOWNLOAD_PATH}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${ASSET_SHA256}" ]; then
|
||||||
|
if [ "${DRY_RUN}" = true ]; then
|
||||||
|
echo "DRY RUN: verify sha256 ${ASSET_SHA256} for ${DOWNLOAD_PATH}"
|
||||||
|
else
|
||||||
|
actual_sha256="$(compute_sha256 "${DOWNLOAD_PATH}" || true)"
|
||||||
|
if [ -z "${actual_sha256}" ]; then
|
||||||
|
log_warn "No checksum command available; skipping digest verification."
|
||||||
|
elif [ "${actual_sha256}" != "${ASSET_SHA256}" ]; then
|
||||||
|
log_err "SHA256 mismatch for ${ASSET_NAME}"
|
||||||
|
log_err "Expected: ${ASSET_SHA256}"
|
||||||
|
log_err "Actual: ${actual_sha256}"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
log_ok "Integrity verified (sha256)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "No SHA256 digest available for ${ASSET_NAME}; skipping integrity verification."
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${OS}" = "darwin" ]; then
|
if [ "${OS}" = "darwin" ]; then
|
||||||
install_macos
|
install_macos
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|||||||
|
|
||||||
# Use a fixture latest.json that mirrors what the real release publishes.
|
# Use a fixture latest.json that mirrors what the real release publishes.
|
||||||
FIXTURE="$REPO_ROOT/scripts/fixtures/latest.json"
|
FIXTURE="$REPO_ROOT/scripts/fixtures/latest.json"
|
||||||
|
RELEASE_FIXTURE="$REPO_ROOT/scripts/fixtures/release.json"
|
||||||
|
|
||||||
# The resolver function should be sourced, not invoked end-to-end (no curl).
|
# The resolver function should be sourced, not invoked end-to-end (no curl).
|
||||||
if ! source "$REPO_ROOT/scripts/install.sh" --source-only 2>/dev/null; then
|
if ! source "$REPO_ROOT/scripts/install.sh" --source-only 2>/dev/null; then
|
||||||
@@ -27,6 +28,262 @@ if [[ "$resolved_arm64" != "$expected_arm64" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
release_parsed=$(resolve_release_asset_metadata "$RELEASE_FIXTURE" "linux" "x86_64" "deb")
|
||||||
|
release_name=$(echo "$release_parsed" | sed -n '2p')
|
||||||
|
release_url=$(echo "$release_parsed" | sed -n '3p')
|
||||||
|
release_digest=$(echo "$release_parsed" | sed -n '4p')
|
||||||
|
if [[ "$release_name" != "OpenHuman_0.0.0-test_amd64.deb" ]]; then
|
||||||
|
echo "FAIL: Debian/Ubuntu linux x86_64 should prefer .deb, got $release_name"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$release_url" != "https://example.invalid/OpenHuman_0.0.0-test_amd64.deb" ]]; then
|
||||||
|
echo "FAIL: expected .deb URL, got $release_url"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$release_digest" != "deb-amd64" ]]; then
|
||||||
|
echo "FAIL: expected .deb digest, got $release_digest"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
release_arm64_parsed=$(resolve_release_asset_metadata "$RELEASE_FIXTURE" "linux" "aarch64" "deb")
|
||||||
|
release_arm64_name=$(echo "$release_arm64_parsed" | sed -n '2p')
|
||||||
|
if [[ "$release_arm64_name" != "OpenHuman_0.0.0-test_arm64.deb" ]]; then
|
||||||
|
echo "FAIL: Debian/Ubuntu linux aarch64 should prefer .deb, got $release_arm64_name"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
release_appimage_parsed=$(resolve_release_asset_metadata "$RELEASE_FIXTURE" "linux" "x86_64" "appimage")
|
||||||
|
release_appimage_name=$(echo "$release_appimage_parsed" | sed -n '2p')
|
||||||
|
if [[ "$release_appimage_name" != "OpenHuman_0.0.0-test_amd64.AppImage" ]]; then
|
||||||
|
echo "FAIL: AppImage linux x86_64 should still resolve AppImage, got $release_appimage_name"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
(
|
||||||
|
tmpdir="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmpdir"' EXIT
|
||||||
|
|
||||||
|
release_without_deb="$tmpdir/release-without-deb.json"
|
||||||
|
cat >"$release_without_deb" <<'JSON'
|
||||||
|
{
|
||||||
|
"tag_name": "v0.0.0-test",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"name": "OpenHuman_0.0.0-test_amd64.AppImage",
|
||||||
|
"browser_download_url": "https://example.invalid/OpenHuman_0.0.0-test_amd64.AppImage",
|
||||||
|
"digest": "sha256:appimage-amd64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
latest_with_appimage="$tmpdir/latest-with-appimage.json"
|
||||||
|
cat >"$latest_with_appimage" <<'JSON'
|
||||||
|
{
|
||||||
|
"version": "0.0.0-test",
|
||||||
|
"platforms": {
|
||||||
|
"linux-x86_64": {
|
||||||
|
"url": "https://example.invalid/OpenHuman_0.0.0-test_amd64.AppImage",
|
||||||
|
"signature": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
cat >"$tmpdir/uname" <<'SH'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
case "$1" in
|
||||||
|
-s) printf 'Linux\n' ;;
|
||||||
|
-m) printf 'x86_64\n' ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
SH
|
||||||
|
chmod +x "$tmpdir/uname"
|
||||||
|
|
||||||
|
cat >"$tmpdir/curl" <<SH
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
output=""
|
||||||
|
args="\$*"
|
||||||
|
while [ "\$#" -gt 0 ]; do
|
||||||
|
if [ "\$1" = "-o" ]; then
|
||||||
|
shift
|
||||||
|
output="\$1"
|
||||||
|
fi
|
||||||
|
shift || true
|
||||||
|
done
|
||||||
|
case " \$args " in
|
||||||
|
*"api.github.com/repos/tinyhumansai/openhuman/releases/latest"*)
|
||||||
|
cp "$release_without_deb" "\$output"
|
||||||
|
;;
|
||||||
|
*"github.com/tinyhumansai/openhuman/releases/latest/download/latest.json"*)
|
||||||
|
cp "$latest_with_appimage" "\$output"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
SH
|
||||||
|
chmod +x "$tmpdir/curl"
|
||||||
|
touch "$tmpdir/apt-get" "$tmpdir/dpkg"
|
||||||
|
chmod +x "$tmpdir/apt-get" "$tmpdir/dpkg"
|
||||||
|
|
||||||
|
set +e
|
||||||
|
explicit_deb_output=$(
|
||||||
|
PATH="$tmpdir:$PATH" OPENHUMAN_INSTALLER_LINUX_PACKAGE=deb bash "$REPO_ROOT/scripts/install.sh" --dry-run 2>&1
|
||||||
|
)
|
||||||
|
explicit_deb_rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ "$explicit_deb_rc" -ne 0 ]]; then
|
||||||
|
echo "FAIL: explicit .deb dry-run should report no .deb asset without failing the smoke query"
|
||||||
|
echo "$explicit_deb_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$explicit_deb_output" != *"no .deb asset resolved"* ]]; then
|
||||||
|
echo "FAIL: explicit .deb dry-run should clearly report that no .deb asset was resolved"
|
||||||
|
echo "$explicit_deb_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$explicit_deb_output" == *"AppImage"* ]]; then
|
||||||
|
echo "FAIL: explicit .deb selection must not fall back to AppImage"
|
||||||
|
echo "$explicit_deb_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set +e
|
||||||
|
explicit_deb_install_output=$(
|
||||||
|
PATH="$tmpdir:$PATH" OPENHUMAN_INSTALLER_LINUX_PACKAGE=deb bash "$REPO_ROOT/scripts/install.sh" 2>&1
|
||||||
|
)
|
||||||
|
explicit_deb_install_rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ "$explicit_deb_install_rc" -eq 0 ]]; then
|
||||||
|
echo "FAIL: explicit .deb install should fail when no .deb asset is resolved"
|
||||||
|
echo "$explicit_deb_install_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$explicit_deb_install_output" != *"Set OPENHUMAN_INSTALLER_LINUX_PACKAGE=appimage"* ]]; then
|
||||||
|
echo "FAIL: explicit .deb install should explain how to opt into the AppImage fallback"
|
||||||
|
echo "$explicit_deb_install_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
|
if ! declare -F install_linux >/dev/null; then
|
||||||
|
echo "FAIL: scripts/install.sh --source-only should expose install_linux for dry-run coverage"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
(
|
||||||
|
HOME="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$HOME"' EXIT
|
||||||
|
OS=linux
|
||||||
|
ARCH=x86_64
|
||||||
|
ASSET_NAME="OpenHuman_0.0.0-test_amd64.deb"
|
||||||
|
DOWNLOAD_PATH="/tmp/OpenHuman_0.0.0-test_amd64.deb"
|
||||||
|
DRY_RUN=true
|
||||||
|
install_output="$(install_linux)"
|
||||||
|
if [[ "$install_output" != *"apt-get install -y --no-install-recommends ${DOWNLOAD_PATH}"* ]]; then
|
||||||
|
echo "FAIL: .deb dry-run should install with apt-get so dependencies resolve"
|
||||||
|
echo "$install_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
|
python3 - "$REPO_ROOT/app/src-tauri/tauri.conf.json" <<'PY'
|
||||||
|
import json, sys
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
with open(path, encoding="utf-8") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
depends = set(config["bundle"]["linux"]["deb"]["depends"])
|
||||||
|
required = {
|
||||||
|
"libatk-bridge2.0-0",
|
||||||
|
"libgbm1",
|
||||||
|
"libnspr4",
|
||||||
|
"libnss3",
|
||||||
|
"libxkbcommon0",
|
||||||
|
"libxshmfence1",
|
||||||
|
}
|
||||||
|
missing = sorted(required - depends)
|
||||||
|
if missing:
|
||||||
|
print("FAIL: .deb package is missing CEF runtime dependencies: " + ", ".join(missing))
|
||||||
|
sys.exit(1)
|
||||||
|
PY
|
||||||
|
|
||||||
|
(
|
||||||
|
tmpdir="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$tmpdir"' EXIT
|
||||||
|
|
||||||
|
latest_complete="$tmpdir/latest-complete.json"
|
||||||
|
cat >"$latest_complete" <<'JSON'
|
||||||
|
{
|
||||||
|
"version": "0.0.0-test",
|
||||||
|
"platforms": {
|
||||||
|
"darwin-aarch64": {"url": "https://example.invalid/OpenHuman_0.0.0-test_aarch64.app.tar.gz"},
|
||||||
|
"darwin-x86_64": {"url": "https://example.invalid/OpenHuman_0.0.0-test_x86_64-apple-darwin.app.tar.gz"},
|
||||||
|
"linux-x86_64": {"url": "https://example.invalid/OpenHuman_0.0.0-test_amd64.AppImage"},
|
||||||
|
"linux-aarch64": {"url": "https://example.invalid/OpenHuman_0.0.0-test_aarch64.AppImage"},
|
||||||
|
"windows-x86_64": {"url": "https://example.invalid/OpenHuman_0.0.0-test_x64.msi"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
release_good_deb_names="$tmpdir/release-good-deb-names.json"
|
||||||
|
cat >"$release_good_deb_names" <<'JSON'
|
||||||
|
{
|
||||||
|
"tag_name": "v0.0.0-test",
|
||||||
|
"assets": [
|
||||||
|
{"name": "OpenHuman_0.0.0-test_aarch64.app.tar.gz"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_x86_64-apple-darwin.app.tar.gz"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_amd64.AppImage"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_aarch64.AppImage"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_x64.msi"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_amd64.deb"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_arm64.deb"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
if ! "$REPO_ROOT/scripts/validate-release-assets.sh" "$release_good_deb_names" "$latest_complete" >/dev/null; then
|
||||||
|
echo "FAIL: release validation should accept OpenHuman-named .deb assets"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
release_bad_deb_names="$tmpdir/release-bad-deb-names.json"
|
||||||
|
cat >"$release_bad_deb_names" <<'JSON'
|
||||||
|
{
|
||||||
|
"tag_name": "v0.0.0-test",
|
||||||
|
"assets": [
|
||||||
|
{"name": "OpenHuman_0.0.0-test_aarch64.app.tar.gz"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_x86_64-apple-darwin.app.tar.gz"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_amd64.AppImage"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_aarch64.AppImage"},
|
||||||
|
{"name": "OpenHuman_0.0.0-test_x64.msi"},
|
||||||
|
{"name": "custom_amd64.deb"},
|
||||||
|
{"name": "custom_arm64.deb"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
JSON
|
||||||
|
|
||||||
|
set +e
|
||||||
|
bad_deb_validation_output=$(
|
||||||
|
"$REPO_ROOT/scripts/validate-release-assets.sh" "$release_bad_deb_names" "$latest_complete" 2>&1
|
||||||
|
)
|
||||||
|
bad_deb_validation_rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ "$bad_deb_validation_rc" -eq 0 ]]; then
|
||||||
|
echo "FAIL: release validation should reject non-OpenHuman .deb asset names"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$bad_deb_validation_output" != *"Missing required release assets"* ]]; then
|
||||||
|
echo "FAIL: release validation should explain missing required .deb assets"
|
||||||
|
echo "$bad_deb_validation_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
missing_channel_output=$(bash "$REPO_ROOT/scripts/install.sh" --channel 2>&1)
|
missing_channel_output=$(bash "$REPO_ROOT/scripts/install.sh" --channel 2>&1)
|
||||||
missing_channel_rc=$?
|
missing_channel_rc=$?
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ ASSET_PATTERNS = {
|
|||||||
"windows-x86_64": r"x64.*\.msi$|x64.*setup\.exe$",
|
"windows-x86_64": r"x64.*\.msi$|x64.*setup\.exe$",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REQUIRED_RELEASE_ASSETS = {
|
||||||
|
"linux-x86_64 .deb": r"OpenHuman_.*_amd64\.deb$",
|
||||||
|
"linux-aarch64 .deb": r"OpenHuman_.*_(arm64|aarch64)\.deb$",
|
||||||
|
}
|
||||||
|
|
||||||
release_path, latest_path = sys.argv[1], sys.argv[2]
|
release_path, latest_path = sys.argv[1], sys.argv[2]
|
||||||
try:
|
try:
|
||||||
release = json.load(open(release_path))
|
release = json.load(open(release_path))
|
||||||
@@ -96,14 +101,20 @@ missing_assets = [
|
|||||||
p for p in SUPPORTED
|
p for p in SUPPORTED
|
||||||
if not any(re.search(ASSET_PATTERNS[p], n) for n in asset_names)
|
if not any(re.search(ASSET_PATTERNS[p], n) for n in asset_names)
|
||||||
]
|
]
|
||||||
|
missing_required_assets = [
|
||||||
|
label for label, pattern in REQUIRED_RELEASE_ASSETS.items()
|
||||||
|
if not any(re.search(pattern, n) for n in asset_names)
|
||||||
|
]
|
||||||
|
|
||||||
tag = release.get("tag_name") or release.get("name") or "<unknown tag>"
|
tag = release.get("tag_name") or release.get("name") or "<unknown tag>"
|
||||||
if missing_latest or missing_assets:
|
if missing_latest or missing_assets or missing_required_assets:
|
||||||
print(f"Release validation FAILED for {tag}", file=sys.stderr)
|
print(f"Release validation FAILED for {tag}", file=sys.stderr)
|
||||||
if missing_latest:
|
if missing_latest:
|
||||||
print(f" Missing from latest.json: {', '.join(missing_latest)}", file=sys.stderr)
|
print(f" Missing from latest.json: {', '.join(missing_latest)}", file=sys.stderr)
|
||||||
if missing_assets:
|
if missing_assets:
|
||||||
print(f" Missing release assets: {', '.join(missing_assets)}", file=sys.stderr)
|
print(f" Missing release assets: {', '.join(missing_assets)}", file=sys.stderr)
|
||||||
|
if missing_required_assets:
|
||||||
|
print(f" Missing required release assets: {', '.join(missing_required_assets)}", file=sys.stderr)
|
||||||
print(
|
print(
|
||||||
" See scripts/install.sh for the supported-platform matrix.",
|
" See scripts/install.sh for the supported-platform matrix.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
|
|||||||
Reference in New Issue
Block a user