Skip to content

fix: use Homebrew for signal-cli install on non-x64 architectures (bug #15372)#15443

Merged
steipete merged 3 commits intoopenclaw:mainfrom
jogvan-k:fix/signal-cli-arch-aware
Feb 13, 2026
Merged

fix: use Homebrew for signal-cli install on non-x64 architectures (bug #15372)#15443
steipete merged 3 commits intoopenclaw:mainfrom
jogvan-k:fix/signal-cli-arch-aware

Conversation

@jogvan-k
Copy link

@jogvan-k jogvan-k commented Feb 13, 2026

Summary

AI-assisted The signal-cli auto-installer downloads x86-64 GraalVM native binaries regardless of host architecture. On non-x64 systems (e.g., Raspberry Pi aarch64) this causes Exec format error when the binary is executed.
This PR makes pickAsset architecture-aware and adds a Homebrew fallback for non-x64 Linux, since official signal-cli releases only publish native binaries for x86-64.
Closes #15372

Repro Steps

  1. Run OpenClaw on an arm64 Linux system (e.g., Raspberry Pi 4/5)
  2. Trigger signal-cli onboarding / auto-install
  3. The installer downloads signal-cli-*-Linux-native.tar.gz (x86-64 binary)
  4. Running the binary fails with: Exec format error

Root Cause

pickAsset() selected release assets based on OS only, with no architecture check. The Linux-native asset is an x86-64 GraalVM binary, but it was selected on all Linux architectures.
Behavior Changes

  • x64 Linux / macOS / Windows: No change. Downloads the native binary from GitHub releases as before.
  • Non-x64 Linux (arm64, armv7, etc.): pickAsset now returns undefined, and the installer falls back to brew install signal-cli. Homebrew builds from source and bundles the JRE automatically.
  • Non-x64 Linux without Homebrew: Returns a clear error message directing the user to install Homebrew or install signal-cli manually.
  • 15-minute timeout for the brew install path (building from source takes time).
  • Version is extracted from signal-cli --version after brew install.
    Codebase and GitHub Search
  • Searched src/commands/signal-install.ts — the only hardcoded binary installer for signal-cli.
  • Verified src/infra/brew.ts exports resolveBrewExecutable() which handles macOS + Linuxbrew path detection — reused here.
  • Confirmed other tool installers (brew/go/node/uv-based skills) delegate to package managers that handle architecture natively — not affected.
    Tests
    Fully tested. 15 new unit tests in src/commands/signal-install.test.ts (all passing):
  • looksLikeArchive: recognises .tar.gz, .tgz, .zip; rejects .asc and unrelated files
  • pickAsset linux: selects native asset on x64; returns undefined on arm64 and arm (triggers brew fallback)
  • pickAsset darwin: selects macOS asset on both arm64 and x64
  • pickAsset win32: selects Windows asset
  • pickAsset edge cases: empty assets, missing fields, unknown platform, .asc exclusion
    pnpm vitest run src/commands/signal-install.test.ts
    ✓ src/commands/signal-install.test.ts (15 tests) 44ms
    Test Files 1 passed (1)
    Tests 15 passed (15)

Manual Testing

Tested on a Raspberry Pi 4 (aarch64 Linux) — When using openclaw config to set up Signal, brew install signal-cli completes successfully and the installed binary works.

Sign-Off

  • Models used: claude-opus-4.6 (via OpenCode)
  • AI-assisted: Yes — code authored by AI agent, iteratively tested and directed by human on real hardware
  • Degree of testing: Fully tested (15 unit tests + manual verification on Raspberry Pi aarch64)
  • Submitter effort: Collaborative — user reported the bug, tested each iteration on Pi, chose the Homebrew approach over alternatives (JVM archive, community binaries)
  • Agent notes: pickAsset and looksLikeArchive were exported (@internal annotated) to enable direct unit testing without mocking I/O.

Greptile Overview

Greptile Summary

This PR updates signal-cli auto-install to avoid selecting the x86_64 Linux-native asset on non-x64 Linux and instead fall back to installing via Homebrew. It also refactors asset selection into exported helpers (looksLikeArchive, pickAsset) and adds unit tests covering platform/arch-aware asset picking and skipping .asc signatures.

The main flow now chooses between “download from GitHub release assets” and “brew install signal-cli” based on process.platform/process.arch, then returns a SignalInstallResult with the installed binary path and version when available.

Confidence Score: 3/5

  • This PR is close to mergeable but has a couple of real failure modes in the new Homebrew fallback path.
  • Core refactor and tests look consistent, but the brew fallback can incorrectly claim Homebrew is missing (no PATH lookup) and can throw on spawn errors instead of returning a structured failure, which will break the intended installer UX on some systems.
  • src/commands/signal-install.ts

Last reviewed commit: 4ee37d1

(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!

@openclaw-barnacle openclaw-barnacle bot added commands Command implementations size: M labels Feb 13, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +157 to +165
async function installSignalCliViaBrew(runtime: RuntimeEnv): Promise<SignalInstallResult> {
const brewExe = resolveBrewExecutable();
if (!brewExe) {
return {
ok: false,
error: "Signal CLI auto-install is not supported on Windows yet.",
error:
`No native signal-cli build is available for ${process.arch}. ` +
"Install Homebrew (https://brew.sh) and try again, or install signal-cli manually.",
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brew detection misses PATH

installSignalCliViaBrew relies on resolveBrewExecutable() which only checks a few hard-coded locations and HOMEBREW_* env vars. If brew is installed but only available via PATH (common on CI or custom setups), brewExe will be undefined and this returns the "Install Homebrew" error even though Homebrew is present.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/signal-install.ts
Line: 157:165

Comment:
**Brew detection misses PATH**

`installSignalCliViaBrew` relies on `resolveBrewExecutable()` which only checks a few hard-coded locations and `HOMEBREW_*` env vars. If `brew` is installed but only available via `PATH` (common on CI or custom setups), `brewExe` will be `undefined` and this returns the "Install Homebrew" error even though Homebrew is present.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 016cbf0. Added findBrewInPath() that runs which brew as a fallback when resolveBrewExecutable() returns undefined. The lookup chain is now: hardcoded paths + HOMEBREW_* env vars → PATH lookup → error.

Comment on lines +168 to 177
runtime.log(`Installing signal-cli via Homebrew (${brewExe})…`);
const result = await runCommandWithTimeout([brewExe, "install", "signal-cli"], {
timeoutMs: 15 * 60_000, // brew builds from source; can take a while
});

if (result.code !== 0) {
return {
ok: false,
error: `brew install signal-cli failed (exit ${result.code}): ${result.stderr.trim().slice(0, 200)}`,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brew spawn errors escape

The runCommandWithTimeout([brewExe, "install", "signal-cli"]) call isn’t wrapped in try/catch, but runCommandWithTimeout rejects on spawn errors (missing executable, permission issues, etc.). That means installSignalCliViaBrew can throw instead of returning a structured { ok: false, error }, and the caller only sees a generic exception string.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/signal-install.ts
Line: 168:177

Comment:
**Brew spawn errors escape**

The `runCommandWithTimeout([brewExe, "install", "signal-cli"])` call isn’t wrapped in `try/catch`, but `runCommandWithTimeout` rejects on spawn errors (missing executable, permission issues, etc.). That means `installSignalCliViaBrew` can throw instead of returning a structured `{ ok: false, error }`, and the caller only sees a generic exception string.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 016cbf0. The runCommandWithTimeout call is now wrapped in try/catch and returns a structured { ok: false, error } on spawn failures.

@steipete steipete force-pushed the fix/signal-cli-arch-aware branch from 016cbf0 to 78a7a30 Compare February 13, 2026 13:25
@steipete steipete merged commit 8307f97 into openclaw:main Feb 13, 2026
3 checks passed
@steipete
Copy link
Contributor

Landed via temp rebase onto main.

  • Gate: pnpm check && pnpm check:docs && pnpm build && pnpm test
  • Land commit: 78a7a30
  • Merge commit: 8307f97

Thanks @jogvan-k!

alex-muradov pushed a commit to alex-muradov/openclaw that referenced this pull request Feb 13, 2026
zhangyang-crazy-one pushed a commit to zhangyang-crazy-one/openclaw that referenced this pull request Feb 13, 2026
skyhawk14 pushed a commit to skyhawk14/openclaw that referenced this pull request Feb 13, 2026
steipete added a commit to azade-c/openclaw that referenced this pull request Feb 14, 2026
GwonHyeok pushed a commit to learners-superpumped/openclaw that referenced this pull request Feb 15, 2026
cloud-neutral pushed a commit to cloud-neutral-toolkit/openclawbot.svc.plus that referenced this pull request Feb 15, 2026
jiulingyun added a commit to jiulingyun/openclaw-cn that referenced this pull request Feb 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug]: Signal-CLI compatibility issue on Raspberry Pi (aarch64)

3 participants

Comments