Skip to content

fix(desktop): bundle the iRedMail engine and point the API at it#238

Open
luckycrm wants to merge 1 commit into
oblien:mainfrom
luckycrm:fix/desktop-bundle-mail-engine
Open

fix(desktop): bundle the iRedMail engine and point the API at it#238
luckycrm wants to merge 1 commit into
oblien:mainfrom
luckycrm:fix/desktop-bundle-mail-engine

Conversation

@luckycrm

@luckycrm luckycrm commented Jul 26, 2026

Copy link
Copy Markdown

What was broken

Mail server setup fails at step 7 in the packaged desktop app (0.3.0, macOS):

Setup failed
Engine transfer failed: tar failed (exit 1): tar: could not chdir to '/Users/<user>/Library/apps/email/engine'

Two separate causes, both only reachable in a packaged build:

  1. The engine tree is never bundled. build/stage.ts stages the API binary, dashboard, migrations and pglite assets, but not apps/email/engine. Nothing in Openship.app/Contents/Resources contains iRedMail.sh.

  2. The path the API falls back to is wrong there. resolveLocalEngineDir() (apps/api/src/modules/mail/mail.service.ts) uses resolve(process.cwd(), "../../apps/email/engine"), which is correct only when the API runs from apps/api in the monorepo. services.ts spawns it with cwd: userData, so it resolves to ~/Library/apps/email/engine — two levels above ~/Library/Application Support/Openship. Confirmed against the shipped binary and the running process:

    $ lsof -p <openship-api pid> | awk '$4=="cwd"'
    ... /Users/<user>/Library/Application Support/Openship
    

bun dev never hits either one, because the dev layout resolves the same tree from the checkout.

The change

Stage apps/email/engine as resources/mail-engine (1.5 MB) and hand it to the API via MAIL_SERVER_ENGINE_DIR at spawn — the same stage → extraResource → env-var pattern already used for OPENSHIP_MIGRATIONS_DIR and OPENSHIP_PGLITE_ASSETS_DIR. MAIL_SERVER_ENGINE_DIR was already supported by resolveLocalEngineDir(); nothing set it. Dev behaviour is unchanged: with the variable unset, the monorepo-relative fallback still applies.

stepTransferEngine now also checks the directory exists before calling transferIn. transferIn shells out to tar, so a missing engine surfaced as a bare tar exit 1, which reads like an SSH/transport failure rather than a missing directory.

Verification

Live run. Against the shipped 0.3.0 app on macOS 15 targeting an Ubuntu 24.04 DigitalOcean droplet, with MAIL_SERVER_ENGINE_DIR pointed at a copy of the engine tree — i.e. exactly the state this PR produces automatically. Step 7, which previously failed outright, now completes:

Transferring engine /Users/<user>/Library/Application Support/Openship/mail-engine → /root/iRedMail-engine...
Packing source into a single archive...
Uploading 200 KB archive via rsync (resumable)...
Transferred 200 KB and extracted on the server.
Engine staged

Step 8 then passes as well (iRedMail.sh found, made executable). Setup proceeds to step 9.

Build. bun run build/stage.ts (with the pinned Bun 1.3.3 from .bun-version) completes and produces resources/mail-engine/iRedMail.sh; the ssh2 #25500 canary still passes.

Test. New apps/desktop/test/bundled-resources.test.ts — a static contract scan in the style of the existing ipc-contract.test.ts, checking that everything stage.ts stages is shipped by forge.config.js and, for the engine specifically, that resourcePaths() exposes it and the API is spawned with MAIL_SERVER_ENGINE_DIR. Reverting the three source files and keeping the test reproduces the failure:

× the mail engine is staged, shipped, and located for the API (regression: setup step 7)
  AssertionError: stage.ts does not stage resources/mail-engine: expected false to be true

With the fix: 6 passed.

Suite. bun run test — 7/7 workspace tasks, 969 API tests passing. bun run --cwd apps/desktop lint and bun run --cwd apps/api lint clean. bun format reformats a lot of pre-existing drift repo-wide; per CONTRIBUTING I dropped all of it and confirmed with prettier --check that none of the reformatting falls on lines this PR adds.

Separate issue found downstream (not addressed here)

With the transfer fixed, setup reaches step 9 and fails there for an unrelated reason — the vendored engine is iRedMail 1.8.1, and the installer's own online version check rejects it:

[ INFO ] Checking new version of iRedMail ...
<< ERROR >> Your iRedMail version (1.8.1) is out of date, please
<< ERROR >> download the latest version and try again:
Installer exited with code 255

check_new_iredmail() in pkgs/get_all.sh queries l.iredmail.org and exit 255 when upstream reports UPDATE_AVAILABLE. Upstream is currently 1.8.4, so mail setup cannot complete on any build — packaged or dev — until apps/email/engine is bumped. Replacing the tree with 1.8.4 locally gets past it; all the config keys the mail service generates (BACKEND, FIRST_DOMAIN, STORAGE_BASE_DIR, USE_FAIL2BAN, the DB password vars, DOMAIN_ADMIN_PASSWD_PLAIN, and the six AUTO_* flags) still exist in 1.8.4, and it supports Ubuntu 24.04. That's a vendored-dependency bump and a different change, so it's left out of this PR — happy to open it separately if you'd like.

I have not run a full make + complete iRedMail install end to end, for the reason above.

Mail server setup failed at step 7 in the packaged desktop app with
"Engine transfer failed: tar failed (exit 1): tar: could not chdir to
'<home>/Library/apps/email/engine'".

The API resolves the engine tree with a cwd-relative fallback,
resolve(process.cwd(), "../../apps/email/engine"), which only lands
correctly when it runs from apps/api in the monorepo. The desktop spawns
the API with cwd = userData, so on macOS that resolves two levels up to
~/Library/apps/email/engine. The tree was also never staged into the app
bundle, so no path would have found it.

Ship apps/email/engine as resources/mail-engine (1.5 MB) and set
MAIL_SERVER_ENGINE_DIR at spawn, matching how migrations and the pglite
assets are already staged and located. Dev is unaffected: without the env
var the monorepo-relative fallback still applies.

Also return a real error when the engine directory is absent. transferIn
shells out to tar, so the failure surfaced as an opaque tar exit 1 that
reads like an SSH/transport problem rather than a missing directory.
@Hydralerne

Copy link
Copy Markdown
Member

nice one, gonna check out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants