Skip to content

feat: add a runtime Towerfile field and a bun runtime - #380

Open
migueltheduck wants to merge 3 commits into
fix/bash-runtimefrom
feat/bun-runtime
Open

migueltheduck wants to merge 3 commits into
fix/bash-runtimefrom
feat/bun-runtime

Conversation

@migueltheduck

@migueltheduck migueltheduck commented Nov 10, 2026 •

Copy link
Copy Markdown

Stacked on #379 (base fix/bash-runtime). GitHub retargets this to develop once that merges.

Why

The runtime picks how to start an app from the script's extension: .sh runs with bash, anything else with uv. There's no way to run a JavaScript or TypeScript app. The bundle also doesn't say which runtime an app needs, so the server can't send its runs to a runner that has it.

What

  • [app] runtime: python, bash or bun. Unknown values are rejected, and bun needs a JavaScript or TypeScript script. Without the field, .sh runs with bash and everything else with Python, as before.
  • MANIFEST: the value is copied only when it's set. It stays a string, so a runtime this CLI doesn't know fails the run with UnsupportedRuntime instead of failing to parse.
  • Bun runtime: when the app has a package.json, bun install runs first, frozen when there's a lockfile. Then bun run <script> runs in its own process group, with a cleared environment in protected mode, like uv. If bun isn't installed, the run fails with MissingBun.
  • import_paths go on NODE_PATH, as Python's go on PYTHONPATH, so ../shared/helpers.ts is imported as "helpers". The app's node_modules goes on it too, so shared code gets the versions the app pinned; otherwise bun would quietly auto-install the latest.
  • Packaging leaves node_modules out of bundles, as it does .venv.
  • Agents:
    • tower_file_update takes runtime.
    • When there's no pyproject.toml, tower_file_generate writes a bun Towerfile from package.json. It takes the script from module, main or the start script, then falls back to common entry points.
    • The workflow help and the skill cover bun init.
  • CI installs bun on Linux, so the bun tests run there.

Compatibility

Apps that don't set runtime are unaffected: no new key in the MANIFEST, still version 3, and the same runtime choice as before. Servers and runners that don't know the key ignore it. tower_file_generate still prefers pyproject.toml; with no pyproject.toml and no package.json, the error now names both files.

migueltheduck and others added 3 commits November 4, 2026 16:43
`[app] runtime` declares what starts the script: python (uv), bash or
bun. It is copied into the MANIFEST only when set, so existing bundles
are unchanged. Without it the runtime still follows the script: .sh runs
with bash, anything else with Python. The manifest keeps the value as a
string, so a runtime this version doesn't know still parses and fails
the run with UnsupportedRuntime instead.

Bun apps run `bun install` (frozen when there's a lockfile) when they
have a package.json, on the setup channel, then `bun run` on the script.
Like uv and bash, bun runs in its own process group and with a cleared
environment in protected mode, and installs into the runtime's cache dir
when it has one. Bun scripts must be JavaScript or TypeScript, and
import_paths aren't supported with bun yet.

The packager now leaves node_modules out of bundles, and the MCP
tower_file_update tool and the agent skill cover the new key.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The runtime puts a bun app's import paths on NODE_PATH, as it puts a
Python app's on PYTHONPATH, so `import_paths = ["../shared"]` makes
`../shared/helpers.ts` importable as "helpers".

Shared code is packaged under modules/, outside the app directory, so
it never walks up into the app's node_modules. Bun would then quietly
auto-install the latest version of whatever the shared code imports.
The app's node_modules goes on NODE_PATH after the import paths, so
shared code gets the versions the app pinned.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
tower_file_generate still reads pyproject.toml when there is one.
Without it, it reads package.json and writes a bun Towerfile: the name
without its npm scope, and the script from `module` (what `bun init`
writes), `main` or the `start` script, then common entry points. A
`main` that names a build output that doesn't exist yet is skipped.

The workflow help and the skill gain a `bun init -y` step.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Nov 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@github-actions

Copy link
Copy Markdown
Contributor


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

This branch has not been deployed

No deployments
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