Skip to content

e2e: packaged-desktop target — the real bundle attaches to the supervised daemon - #1017

Merged
RhysSullivan merged 1 commit into
mainfrom
claude/packaged-desktop-e2e
Jun 14, 2026
Merged

e2e: packaged-desktop target — the real bundle attaches to the supervised daemon#1017
RhysSullivan merged 1 commit into
mainfrom
claude/packaged-desktop-e2e

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

What

Adds an e2e target that drives the real electron-builder bundle (not dev
electron), because the supervised-daemon attach path and the bundled compiled
sidecar only run when app.isPackaged is true.

In dev, boot() skips ensureSupervisedConnection and always spawns a fresh
desktop-sidecar (rewriting the server manifest). So the attach behaviour —
the always-on promise that the installed app connects to an OS-supervised
daemon instead of owning its own server — cannot be proven against a dev
launch. The previous dev-electron attach test could never satisfy its own
assertion; this replaces it with one against the production artifact.

Changes

  • apps/desktop/electron-builder.e2e.config.ts — unsigned packaging config
    (no notarize / hardenedRuntime, dir target) so the real bundle builds
    without Apple/Windows signing credentials. The release config is untouched.
  • desktop-packaged vitest project + globalsetup — builds web UI → compiled
    sidecar → electron-vite → electron-builder, then publishes the launch
    executable and the bundled sidecar path.
  • desktop-packaged/supervised-attach.test.ts — starts the bundle's own
    compiled executor-sidecar as the supervised daemon, launches the packaged
    app at the same HOME, and asserts it attached (the manifest still names
    the daemon's pid and kind: cli-daemon; the bearer-gated console renders)
    rather than spawning its own sidecar.
  • Removes the superseded dev-electron attach test; fixes a stale comment
    ("Basic-auth" → the header is a bearer).

Honesty note on coverage

Driving the packaged Electron window needs a real window-server session (Aqua on
macOS; an X/Wayland display on Linux). In a background/SSH/CI shell there is no
such session and Electron can't open a window, so the scenario skips with a
clear reason
there instead of hanging — it runs for real on a logged-in
console or a guest under autologin/Xvfb. The supervised daemon's reboot-survival
(install → real machine reboot → auto-start → data intact) is separately proven
green on real VMs by the cli-macos, cli-linux, and cli-windows targets.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing 6717599 Commit Preview URL

Branch Preview URL
Jun 14 2026, 04:52 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 6717599 Jun 14 2026, 04:53 PM

@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@pkg-pr-new

pkg-pr-new Bot commented Jun 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@1017

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@1017

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@1017

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@1017

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@1017

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@1017

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@1017

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@1017

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@1017

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@1017

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@1017

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@1017

executor

npm i https://pkg.pr.new/executor@1017

commit: 6717599

…ised daemon

The supervised-daemon attach path (ensureSupervisedConnection →
attachToSupervisedDaemon) and the bundled compiled sidecar only run when
app.isPackaged is true. Dev electron skips that branch and always spawns its
own desktop-sidecar, so the attach behaviour can't be proven against a dev
launch — the prior dev-electron attach test could never satisfy its own
assertion. Test the production artifact instead.

- electron-builder.e2e.config.ts: unsigned bundle (no notarize/hardenedRuntime,
  `dir` target) so the real package builds without Apple credentials; the
  release config is untouched.
- desktop-packaged vitest project + globalsetup: builds web UI → compiled
  sidecar → electron-vite → electron-builder, then publishes the launch exe and
  bundled sidecar path.
- desktop-packaged/supervised-attach.test.ts: starts the bundle's own compiled
  executor-sidecar as the supervised daemon, launches the packaged app, and
  asserts it attached (manifest still names the daemon's pid/kind, console
  renders through the bearer-gated daemon) rather than spawning a sidecar.
  Skips honestly where no GUI display is reachable instead of hanging on launch.
- Remove the superseded dev-electron attach test; fix a stale "Basic-auth"
  comment (the header is bearer).
@RhysSullivan
RhysSullivan force-pushed the claude/packaged-desktop-e2e branch from 82b86ae to 6717599 Compare June 14, 2026 16:51
@RhysSullivan
RhysSullivan merged commit 994e5b9 into main Jun 14, 2026
14 checks passed
@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Greptile Summary

Adds a desktop-packaged e2e target that builds and drives the real electron-builder bundle — the only path where app.isPackaged is true and ensureSupervisedConnection runs — replacing a dev-electron attach test that could never exercise the production attach path. Also fixes a stale comment in index.ts ("Basic-auth" → "bearer-auth").

  • New e2e project (desktop-packaged): globalsetup builds the full unsigned bundle (web UI → compiled sidecar → electron-vite → electron-builder dir target), then the test starts the bundled executor-sidecar as a supervised daemon and asserts the packaged app attaches to it (same pid + kind: cli-daemon in the manifest) rather than spawning its own sidecar.
  • GUI-availability guard: the test skips gracefully on headless/SSH environments where Electron can't open a window, wired through it.skip.
  • Old dev-electron test removed: the prior test launched dev Electron (which always skips ensureSupervisedConnection), so its attach assertion was structurally unreachable; this PR correctly retires it.

Confidence Score: 3/5

Safe to merge for macOS/Linux; the finally-block cleanup in the new test has a race that can mask failures or produce spurious errors on Windows.

The core logic is solid and fills a genuine coverage gap. The concrete defect is in the finally block: daemon?.kill(SIGTERM) is fire-and-forget and rmSync(home) follows immediately. On Windows, TerminateProcess is async and file handles may still be held open when rmSync runs; a locked-handle error thrown from finally either surfaces as a false test failure or swallows the real failure message.

e2e/desktop-packaged/supervised-attach.test.ts — the finally-block cleanup ordering

Important Files Changed

Filename Overview
e2e/desktop-packaged/supervised-attach.test.ts New packaged-Electron attach test. Correct overall shape, but the finally block kills the daemon then immediately deletes the home directory without waiting for process exit — can cause rmSync to throw on Windows (locked handles) and mask actual test failures. Also reads server.json directly, which crosses the AGENTS.md black-box rule.
e2e/setup/desktop-packaged.globalsetup.ts Global setup: sequentially builds web UI → compiled sidecar → electron-vite → electron-builder (unsigned dir target), publishes exe/sidecar paths via process.env. Platform path logic for macOS/Linux/Windows looks correct; SKIP_BUILD guard logic is sound.
apps/desktop/electron-builder.e2e.config.ts Unsigned e2e packaging config that overrides the release config for all three platforms: disables signing/notarize, uses dir target, pins executableName on Linux. Clean and well-commented; release config is untouched.
apps/desktop/src/main/index.ts Single comment correction: Basic-auth → bearer-auth. No logic change.
e2e/targets/registry.ts Registers desktop-packaged target reusing desktopTarget — correct, since per-scenario app launches need no shared surface.
e2e/vitest.config.ts Adds desktop-packaged vitest project with correct include pattern, generous timeouts (360s test / 600s hook for the long build globalsetup), and proper globalSetup path wiring.
e2e/desktop/supervised-attach.test.ts Deleted — the dev-electron attach test that could never exercise ensureSupervisedConnection (which is skipped when app.isPackaged is false). Correct removal.

Sequence Diagram

sequenceDiagram
    participant GS as globalsetup
    participant EB as electron-builder
    participant Test as supervised-attach.test
    participant Daemon as executor-sidecar (supervised)
    participant App as Packaged Electron App

    GS->>EB: build unsigned bundle (dir target)
    EB-->>GS: "exe + sidecar paths -> process.env"

    Test->>Daemon: "spawn sidecarBin (EXECUTOR_SUPERVISED=1)"
    Daemon-->>Test: stdout: EXECUTOR_READY
    Daemon->>Daemon: "write server.json (kind=cli-daemon, pid=N)"

    Test->>Test: waitForHttp (port ready)
    Test->>Test: "read server.json -> daemonPid=N"

    Test->>App: "_electron.launch(executablePath, HOME=home)"
    Note over App: app.isPackaged=true -> ensureSupervisedConnection()
    App->>Daemon: "reads server.json -> connects with bearer token"
    App-->>Test: firstWindow()

    Test->>Test: "waitFor Settings text -> attach proven via UI"
    Test->>Test: "read server.json -> assert kind=cli-daemon, pid=N unchanged"

    Note over Test,App: finally block
    Test->>App: app.close()
    Test->>Daemon: kill(SIGTERM)
    Test->>Test: rmSync(home)
Loading

Reviews (1): Last reviewed commit: "e2e: packaged-desktop target — the real ..." | Re-trigger Greptile

Comment on lines +187 to +213
} finally {
const page = app?.windows()[0];
const video = page?.video();
await app?.close().catch(() => {});
const recordedPath = await video?.path().catch(() => undefined);
if (recordedPath && existsSync(recordedPath)) {
await promisify(execFile)("ffmpeg", [
"-y",
"-i",
recordedPath,
"-c:v",
"libx264",
"-preset",
"veryfast",
"-crf",
"26",
"-pix_fmt",
"yuv420p",
"-movflags",
"+faststart",
join(runDir, "session.mp4"),
]).catch(() => {});
}
daemon?.kill("SIGTERM");
rmSync(videoTmp, { recursive: true, force: true });
rmSync(home, { recursive: true, force: true });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Daemon not awaited before home directory is deleted

daemon?.kill("SIGTERM") sends the signal but returns immediately; rmSync(home, ...) executes synchronously right after with no guarantee the daemon process has actually exited. On Windows, where kill("SIGTERM") calls TerminateProcess(), the OS may still hold file handles open during teardown — causing rmSync to throw EACCES or EBUSY since { force: true } only suppresses ENOENT, not locked-file errors. A throw from a finally block swallows the original test failure and can turn a passing test into a spurious failure.

Comment on lines +146 to +150
const daemonManifest = JSON.parse(readFileSync(manifestPath, "utf8")) as Manifest;
expect(daemonManifest.kind, "the compiled sidecar advertises itself as cli-daemon").toBe(
"cli-daemon",
);
const daemonPid = daemonManifest.pid;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Direct filesystem read of an internal implementation file

e2e/AGENTS.md says tests must be black-box, driving the product only through public surfaces (API, web UI, MCP, CLI) and must never poke internal files. Reading server.json from within home/.executor/server-control/ is reading a private implementation artifact, not a public surface. Is there a health-check endpoint, a CLI command like executor status --json, or any other public surface that exposes which pid/kind is currently serving — so the attachment proof could be expressed without depending on the manifest's internal path and schema?

Context Used: e2e/AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Is there a public CLI command or HTTP endpoint (e.g. GET /status) that reports the running daemon's pid and kind, so the attach assertion could be made through a public surface rather than reading server.json directly?

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.

1 participant