Skip to content

feat: bundle Oliver and Boris inside the app for sandbox self-containment - #211

Merged
drawmeanelephant merged 1 commit into
mainfrom
feat/bundle-helpers
Aug 26, 2026
Merged

drawmeanelephant merged 1 commit into
mainfrom
feat/bundle-helpers

Conversation

@drawmeanelephant

Copy link
Copy Markdown
Owner

Closes #209.

What

BANAL now ships its own Oliver and Boris. make app builds universal (arm64 + x86_64) binaries from the sibling Zig checkouts into Contents/Helpers/, the locators prefer them, and Settings → Publish loses its binary pickers. Recipe Read and Publish Site work in the sandboxed app on a machine with nothing on PATH.

How

  • Scripts/helpers.sh — finds each engine (override dir → sibling checkout → ~/t3/zig/<name>), cross-compiles both arches with pinned Zig + lipo, degrades to host-arch / skip-with-warning. Never fatal: missing engines mean builtin-compiler behavior, same as today.
  • Makefile — copies helpers in and signs each one before the outer seal (unsigned nested executables break codesign).
  • BundledHelper — one lookup: Bundle.main.url(forAuxiliaryExecutable:) plus the plain Contents/Helpers/<name> path. Injected as a closure into OliverLocator/BorisLocator so tests pin the order: configured → bundled → env → PATH → sibling.
  • Settings — the "Boris (optional)" / "Oliver (optional)" rows and their CompilerBookmark plumbing are gone. Legacy paths already in .banal/config.json still round-trip; debug overrides stay via BANAL_OLIVER_BIN / BANAL_BORIS_BIN.
  • Honest script errors — AppleScript command failures set scriptErrorNumber so osascript sees an error instead of a silent missing value. Found while verifying this PR.

Verification

  • New locator-ordering tests (bundled beats env beats PATH; configured still wins) — all green; full suite green except the pre-existing environment-dependent testDoctorFailsWhenConfiguredEngineIsMissing, which fails identically on clean origin/main on machines with boris installed.
  • make smoke: signed app boots against a scratch container vault.
  • End-to-end in the sandboxed signed app, driven via the scripting dictionary: created a note, marked it published, ran publish site → status sentence Published 1 note with Boris. and a real .publish/ site on disk. No PATH binary involved — bundled helper executed under App Sandbox.

Out of scope (filed)

#210 — publishing a mixed vault through Boris fails because recipe pages are written after Boris's link audit. Pre-existing on main (builtin path unaffected); needs a pipeline decision against ROADMAP M4 before bundling can claim mixed-vault publish.

Notes for review

  • The doctor test failure above is environmental, not from this branch — worth its own ticket eventually (it assumes no boris on PATH).
  • dist/helpers/ is a build artifact inside the existing gitignored dist/; nothing prebuilt is committed.

- make app builds universal helper binaries from sibling Zig checkouts
  (Scripts/helpers.sh) into Contents/Helpers, signing each before the
  outer seal; missing checkouts are a warning, never an error
- OliverLocator/BorisLocator resolve the bundled helper first (after a
  configured path), then env/PATH/sibling checkout as before; the
  bundle lookup is injectable for tests (BundledHelper)
- Settings -> Publish loses its Boris/Oliver pickers; legacy paths in
  .banal/config.json still round-trip
- AppleScript command errors now set scriptErrorNumber so failures
  surface in osascript instead of returning missing value
- docs: PREFERENCES, README, STATUS, CHANGELOG updated

Verified end to end: the signed sandboxed app publishes via the bundled
Boris ('Published 1 note with Boris.') against a scratch container vault.

Closes #209
@drawmeanelephant
drawmeanelephant merged commit 66b348e into main Aug 26, 2026
3 checks passed
@drawmeanelephant
drawmeanelephant deleted the feat/bundle-helpers branch August 26, 2026 11:50
@itoqa

itoqa Bot commented Aug 26, 2026

Copy link
Copy Markdown

Ito QA test results
Commit: 9f69917: 14 test cases ran, 14 passed ✅.

Summary

Coverage spans normal recipe reading and publishing flows, helper selection and fallback behavior, cleanup after failed builds, error handling, packaging and sandbox integration, and preservation of publish settings. It includes product logic, edge-case recovery, build-time safeguards, and user-facing workflow behavior, with no observed application regressions.

Safe to merge — no failures are attributable to this PR, so there are no identified regressions or merge-blocking defects. Some native macOS build and UI flows were not executable in the available environment and remain follow-up validation items rather than PR-related failures.

Tests run by Ito

View full run

Result Severity Type Description
General Invalid helper directory settings were skipped, and the next valid sibling checkouts built both helpers successfully.
General When both helper builds failed, the old Oliver and Boris files were removed. No stale helper was left for the app to package or sign.
General Recipe reading can move past an Oliver helper that fails its first check and use the next working helper. If the recipe still cannot be read, the app clears old recipe data and shows a clear message instead of showing stale content.
General The AppleScript command path returns an error message and error number 1 when a command fails, while successful commands still return their result. Native Apple Event execution could not run in this Linux environment, so the behavior was confirmed from the shared production handler instead.
Boris The configured Boris executable is selected before the bundled helper and other available candidates. The Publish Site screen could not run in this Linux environment because the product is a native macOS app, but source inspection and focused test coverage confirm the required selection order.
Build The signed app could not be built in the Linux test environment because the required Swift and macOS signing tools were unavailable. Source review confirms that missing helper checkouts are handled with warnings, so no application failure was observed.
Oliver The recipe screen could not be opened because the local target only provides a static Linux workbench, not the native macOS app. Source review found the Oliver selection and recipe display paths in place, so this case is treated as an environment pass rather than an application failure.
Publish The local check correctly stopped at the platform notice because this Linux target cannot launch the native macOS notes app. No product failure was found.
Recipe Cooklang recipes continue to open in Read mode and show their ingredients, cookware, and steps when a recipe-capable Oliver is available.
Rev The packaging steps are present and put the helper tools inside the app before the app is signed. The check could not launch the signed macOS app because it ran on a Linux machine without Apple's signing tools.
Rev The app includes sandbox access and bundled-helper lookup for recipe reading and site publishing. Native execution could not be performed because the available workbench runs Linux instead of macOS.
Rev The AppleScript publish check could not run because this Linux workbench has no macOS Apple Event tool. Source inspection shows that a publish failure is returned with an error message and a nonzero error code instead of being reported as a successful empty result.
Scripting AppleScript failures are set up to return a clear message, error number 1, and no reply while recording the failure. Native execution was unavailable because the local test environment runs Linux instead of macOS.
Settings The Publish settings keep the site, Cloudflare Pages, configuration, and deployment controls, while the old compiler controls are gone. The browser could not open the native macOS window in the Linux test environment, but the native source confirms the expected layout.

Tip

Reply with @itoqa to send us feedback on this test run.

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.

feat: Bundle Oliver and Boris inside the app for sandbox self-containment

1 participant