From beb8a72d0d94bf1f2afe8bb7886c4b5544442b53 Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 4 Mar 2026 14:21:45 +0100 Subject: [PATCH] feat: add option for process cwd (.env support) --- MODULE.bazel.lock | 4 ++-- README.md | 14 ++++++++++++++ bun/defs.bzl | 1 + internal/bun_binary.bzl | 14 +++++++++++++- internal/bun_bundle.bzl | 1 + internal/bun_dev.bzl | 29 ++++++++++++++++++++--------- internal/bun_install.bzl | 4 ++-- internal/bun_test.bzl | 8 ++++---- tests/binary_test/BUILD.bazel | 14 ++++++++++++++ tests/binary_test/env.ts | 2 ++ tests/binary_test/run_env_binary.sh | 10 ++++++++++ 11 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 tests/binary_test/env.ts create mode 100755 tests/binary_test/run_env_binary.sh diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 51c7a9a..45dfc91 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -189,8 +189,8 @@ "moduleExtensions": { "//bun:extensions.bzl%bun": { "general": { - "bzlTransitiveDigest": "Q0uQOwFAgAU+etePCZ4TUDO+adLX7Z0EmRLaEsKgncw=", - "usagesDigest": "UC4zk8kEwWRiDG5FVQOCFysXcrZ757Jehf3sZgG893w=", + "bzlTransitiveDigest": "oLR98WtKDCc+zh7Tvu9jtakNg8q/T1IPE38QR1FEQtI=", + "usagesDigest": "9s+/cJR+wTifjBrio71E10Knd41B08/GmW4I9zJmU64=", "recordedInputs": [ "REPO_MAPPING:,bazel_tools bazel_tools" ], diff --git a/README.md b/README.md index e5ad3c2..d90ae50 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ Run one of your bun-backed targets, for example: bazel test //path/to:your_bun_test ``` +All `rules_bun` rule-driven Bun invocations pass `--bun`. + ## Development mode (`bun_dev`) Use `bun_dev` for long-running local development with Bun watch mode. @@ -113,6 +115,8 @@ load("@rules_bun//bun:defs.bzl", "bun_dev") bun_dev( name = "web_dev", entry_point = "src/main.ts", + # Optional: run from the entry point directory so Bun auto-loads colocated .env files. + # working_dir = "entry_point", ) ``` @@ -127,6 +131,16 @@ bazel run //path/to:web_dev - `watch_mode = "watch"` (default) for `bun --watch` - `watch_mode = "hot"` for `bun --hot` - `restart_on = [...]` to force full process restarts when specific files change +- `working_dir = "workspace" | "entry_point"` (default: `workspace`) + +## Runtime working directory (`bun_binary`, `bun_dev`) + +`bun_binary` and `bun_dev` support `working_dir`: + +- `"workspace"` (default): runs from the Bazel runfiles workspace root. +- `"entry_point"`: runs from the entry point file's directory. + +Use `"entry_point"` when Bun should resolve local files such as colocated `.env` files relative to the program directory. ### Hybrid Go + Bun + protobuf workflow diff --git a/bun/defs.bzl b/bun/defs.bzl index 739e89a..baf4960 100644 --- a/bun/defs.bzl +++ b/bun/defs.bzl @@ -16,3 +16,4 @@ js_library = _js_library ts_library = _ts_library BunToolchainInfo = _BunToolchainInfo bun_toolchain = _bun_toolchain + \ No newline at end of file diff --git a/internal/bun_binary.bzl b/internal/bun_binary.bzl index f5e76bb..59b6162 100644 --- a/internal/bun_binary.bzl +++ b/internal/bun_binary.bzl @@ -17,10 +17,18 @@ runfiles_dir="${{RUNFILES_DIR:-$0.runfiles}}" bun_bin="${{runfiles_dir}}/_main/{bun_short_path}" entry_point="${{runfiles_dir}}/_main/{entry_short_path}" -exec "${{bun_bin}}" run "${{entry_point}}" "$@" +working_dir="{working_dir}" +if [[ "${{working_dir}}" == "entry_point" ]]; then + cd "$(dirname "${{entry_point}}")" +else + cd "${{runfiles_dir}}/_main" +fi + +exec "${{bun_bin}}" --bun run "${{entry_point}}" "$@" """.format( bun_short_path = bun_bin.short_path, entry_short_path = entry_point.short_path, + working_dir = ctx.attr.working_dir, ), ) @@ -50,6 +58,10 @@ bun_binary = rule( ), "node_modules": attr.label(), "data": attr.label_list(allow_files = True), + "working_dir": attr.string( + default = "workspace", + values = ["workspace", "entry_point"], + ), }, executable = True, toolchains = ["//bun:toolchain_type"], diff --git a/internal/bun_bundle.bzl b/internal/bun_bundle.bzl index 56b6aee..0b45a3f 100644 --- a/internal/bun_bundle.bzl +++ b/internal/bun_bundle.bzl @@ -27,6 +27,7 @@ def _bun_bundle_impl(ctx): outputs.append(output) args = ctx.actions.args() + args.add("--bun") args.add("build") args.add(entry.path) args.add("--outfile") diff --git a/internal/bun_dev.bzl b/internal/bun_dev.bzl index deb9245..5a45c09 100644 --- a/internal/bun_dev.bzl +++ b/internal/bun_dev.bzl @@ -1,6 +1,5 @@ """Rule for running JS/TS scripts with Bun in watch mode for development.""" - def _bun_dev_impl(ctx): toolchain = ctx.toolchains["//bun:toolchain_type"] bun_bin = toolchain.bun.bun_bin @@ -18,7 +17,13 @@ set -euo pipefail runfiles_dir="${{RUNFILES_DIR:-$0.runfiles}}" bun_bin="${{runfiles_dir}}/_main/{bun_short_path}" entry_point="${{runfiles_dir}}/_main/{entry_short_path}" -cd "${{runfiles_dir}}/_main" + +working_dir="{working_dir}" +if [[ "${{working_dir}}" == "entry_point" ]]; then + cd "$(dirname "${{entry_point}}")" +else + cd "${{runfiles_dir}}/_main" +fi watch_mode="{watch_mode}" if [[ "${{watch_mode}}" == "hot" ]]; then @@ -28,7 +33,7 @@ else fi run_dev() {{ - exec "${{bun_bin}}" "${{dev_flag}}" run "${{entry_point}}" "$@" + exec "${{bun_bin}}" --bun "${{dev_flag}}" run "${{entry_point}}" "$@" }} if [[ {restart_count} -eq 0 ]]; then @@ -50,8 +55,9 @@ file_mtime() {{ declare -A mtimes for rel in "${{restart_paths[@]}}"; do - if [[ -e "${{rel}}" ]]; then - mtimes["${{rel}}"]="$(file_mtime "${{rel}}")" + path="${{runfiles_dir}}/_main/${{rel}}" + if [[ -e "${{path}}" ]]; then + mtimes["${{rel}}"]="$(file_mtime "${{path}}")" else mtimes["${{rel}}"]="missing" fi @@ -63,7 +69,7 @@ restart_child() {{ kill "${{child_pid}}" wait "${{child_pid}}" || true fi - "${{bun_bin}}" "${{dev_flag}}" run "${{entry_point}}" "$@" & + "${{bun_bin}}" --bun "${{dev_flag}}" run "${{entry_point}}" "$@" & child_pid=$! }} @@ -82,8 +88,9 @@ while true; do sleep 1 changed=0 for rel in "${{restart_paths[@]}}"; do - if [[ -e "${{rel}}" ]]; then - current="$(file_mtime "${{rel}}")" + path="${{runfiles_dir}}/_main/${{rel}}" + if [[ -e "${{path}}" ]]; then + current="$(file_mtime "${{path}}")" else current="missing" fi @@ -100,6 +107,7 @@ done bun_short_path = bun_bin.short_path, entry_short_path = entry_point.short_path, watch_mode = ctx.attr.watch_mode, + working_dir = ctx.attr.working_dir, restart_count = len(ctx.files.restart_on), restart_watch_paths = restart_watch_paths, ), @@ -121,7 +129,6 @@ done ), ] - bun_dev = rule( implementation = _bun_dev_impl, attrs = { @@ -136,6 +143,10 @@ bun_dev = rule( "restart_on": attr.label_list(allow_files = True), "node_modules": attr.label(), "data": attr.label_list(allow_files = True), + "working_dir": attr.string( + default = "workspace", + values = ["workspace", "entry_point"], + ), }, executable = True, toolchains = ["//bun:toolchain_type"], diff --git a/internal/bun_install.bzl b/internal/bun_install.bzl index db0a01e..f9a5617 100644 --- a/internal/bun_install.bzl +++ b/internal/bun_install.bzl @@ -36,14 +36,14 @@ def _bun_install_repository_impl(repository_ctx): repository_ctx.symlink(bun_lockfile, "bun.lockb") result = repository_ctx.execute( - [str(bun_bin), "install", "--frozen-lockfile", "--no-progress"], + [str(bun_bin), "--bun", "install", "--frozen-lockfile", "--no-progress"], timeout = 600, quiet = False, environment = {"HOME": str(repository_ctx.path("."))}, ) if result.return_code: - fail("""bun_install failed running `bun install --frozen-lockfile`. + fail("""bun_install failed running `bun --bun install --frozen-lockfile`. stdout: {} stderr: diff --git a/internal/bun_test.bzl b/internal/bun_test.bzl index 29d311e..a08f1d3 100644 --- a/internal/bun_test.bzl +++ b/internal/bun_test.bzl @@ -24,15 +24,15 @@ bun_bin="${{runfiles_dir}}/_main/{bun_short_path}" cd "${{runfiles_dir}}/_main" if [[ -n "${{TESTBRIDGE_TEST_ONLY:-}}" && -n "${{COVERAGE_DIR:-}}" ]]; then - exec "${{bun_bin}}" test {src_args} --test-name-pattern "${{TESTBRIDGE_TEST_ONLY}}" --coverage "$@" + exec "${{bun_bin}}" --bun test {src_args} --test-name-pattern "${{TESTBRIDGE_TEST_ONLY}}" --coverage "$@" fi if [[ -n "${{TESTBRIDGE_TEST_ONLY:-}}" ]]; then - exec "${{bun_bin}}" test {src_args} --test-name-pattern "${{TESTBRIDGE_TEST_ONLY}}" "$@" + exec "${{bun_bin}}" --bun test {src_args} --test-name-pattern "${{TESTBRIDGE_TEST_ONLY}}" "$@" fi if [[ -n "${{COVERAGE_DIR:-}}" ]]; then - exec "${{bun_bin}}" test {src_args} --coverage "$@" + exec "${{bun_bin}}" --bun test {src_args} --coverage "$@" fi -exec "${{bun_bin}}" test {src_args} "$@" +exec "${{bun_bin}}" --bun test {src_args} "$@" """.format( bun_short_path = bun_bin.short_path, src_args = src_args, diff --git a/tests/binary_test/BUILD.bazel b/tests/binary_test/BUILD.bazel index 980e2de..369b9c9 100644 --- a/tests/binary_test/BUILD.bazel +++ b/tests/binary_test/BUILD.bazel @@ -43,3 +43,17 @@ sh_test( "//tests/binary_test:BUILD.bazel", ], ) + +bun_binary( + name = "env_cwd_bin", + entry_point = "env.ts", + data = [".env"], + working_dir = "entry_point", +) + +sh_test( + name = "bun_binary_env_cwd_test", + srcs = ["run_env_binary.sh"], + args = ["$(location :env_cwd_bin)"], + data = [":env_cwd_bin"], +) diff --git a/tests/binary_test/env.ts b/tests/binary_test/env.ts new file mode 100644 index 0000000..9ddbf89 --- /dev/null +++ b/tests/binary_test/env.ts @@ -0,0 +1,2 @@ +const value = process.env.BUN_ENV_CWD_TEST ?? "missing"; +console.log(value); diff --git a/tests/binary_test/run_env_binary.sh b/tests/binary_test/run_env_binary.sh new file mode 100755 index 0000000..09c8d25 --- /dev/null +++ b/tests/binary_test/run_env_binary.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +binary="$1" +output="$(${binary})" + +if [[ ${output} != "from-dotenv" ]]; then + echo "Expected .env value from entry-point directory, got: ${output}" >&2 + exit 1 +fi