fix: bun install symlinks
This commit is contained in:
@@ -1,5 +1,30 @@
|
||||
"""Repository-rule based bun_install implementation."""
|
||||
|
||||
_DEFAULT_INSTALL_INPUTS = [
|
||||
".npmrc",
|
||||
"bunfig.json",
|
||||
"bunfig.toml",
|
||||
]
|
||||
|
||||
def _normalize_path(path):
|
||||
normalized = path.replace("\\", "/")
|
||||
if normalized.endswith("/") and normalized != "/":
|
||||
normalized = normalized[:-1]
|
||||
return normalized
|
||||
|
||||
def _relative_to_root(root, child):
|
||||
normalized_root = _normalize_path(root)
|
||||
normalized_child = _normalize_path(child)
|
||||
|
||||
if normalized_child == normalized_root:
|
||||
return ""
|
||||
|
||||
prefix = normalized_root + "/"
|
||||
if not normalized_child.startswith(prefix):
|
||||
fail("bun_install: expected install input {} to be under {}".format(child, root))
|
||||
|
||||
return normalized_child[len(prefix):]
|
||||
|
||||
def _segment_matches(name, pattern):
|
||||
if pattern == "*":
|
||||
return True
|
||||
@@ -87,7 +112,7 @@ def _materialize_workspace_packages(repository_ctx, package_json):
|
||||
if workspace_dir_str == package_root_str:
|
||||
continue
|
||||
|
||||
relative_dir = workspace_dir_str[len(package_root_str) + 1:]
|
||||
relative_dir = _relative_to_root(package_root_str, workspace_dir_str)
|
||||
if relative_dir in written:
|
||||
continue
|
||||
|
||||
@@ -97,6 +122,36 @@ def _materialize_workspace_packages(repository_ctx, package_json):
|
||||
)
|
||||
written[relative_dir] = True
|
||||
|
||||
def _materialize_install_inputs(repository_ctx, package_json):
|
||||
package_root = package_json.dirname
|
||||
package_root_str = str(package_root)
|
||||
written = {}
|
||||
|
||||
for relative_path in _DEFAULT_INSTALL_INPUTS:
|
||||
source_path = repository_ctx.path(str(package_root) + "/" + relative_path)
|
||||
if source_path.exists and not source_path.is_dir:
|
||||
repository_ctx.file(relative_path, repository_ctx.read(source_path))
|
||||
written[relative_path] = True
|
||||
|
||||
for install_input in repository_ctx.attr.install_inputs:
|
||||
source_path = repository_ctx.path(install_input)
|
||||
|
||||
if not source_path.exists:
|
||||
fail("bun_install: install input not found: {}".format(install_input))
|
||||
|
||||
if source_path.is_dir:
|
||||
fail("bun_install: install_inputs must be files under the package root: {}".format(install_input))
|
||||
|
||||
relative_path = _relative_to_root(package_root_str, str(source_path))
|
||||
if not relative_path:
|
||||
fail("bun_install: install input must be a file under the package root: {}".format(install_input))
|
||||
|
||||
if relative_path in written:
|
||||
continue
|
||||
|
||||
repository_ctx.file(relative_path, repository_ctx.read(source_path))
|
||||
written[relative_path] = True
|
||||
|
||||
def _select_bun_binary(repository_ctx):
|
||||
os_name = repository_ctx.os.name.lower()
|
||||
arch = repository_ctx.os.arch.lower()
|
||||
@@ -134,14 +189,23 @@ def _bun_install_repository_impl(repository_ctx):
|
||||
|
||||
repository_ctx.file("package.json", repository_ctx.read(package_json))
|
||||
repository_ctx.symlink(bun_lockfile, lockfile_name)
|
||||
_materialize_install_inputs(repository_ctx, package_json)
|
||||
_materialize_workspace_packages(repository_ctx, package_json)
|
||||
|
||||
result = repository_ctx.execute(
|
||||
[str(bun_bin), "--bun", "install", "--frozen-lockfile", "--no-progress"],
|
||||
timeout = 600,
|
||||
quiet = False,
|
||||
environment = {"HOME": str(repository_ctx.path("."))},
|
||||
)
|
||||
install_args = [str(bun_bin), "--bun", "install", "--frozen-lockfile", "--no-progress"]
|
||||
if repository_ctx.attr.isolated_home:
|
||||
result = repository_ctx.execute(
|
||||
install_args,
|
||||
timeout = 600,
|
||||
quiet = False,
|
||||
environment = {"HOME": str(repository_ctx.path("."))},
|
||||
)
|
||||
else:
|
||||
result = repository_ctx.execute(
|
||||
install_args,
|
||||
timeout = 600,
|
||||
quiet = False,
|
||||
)
|
||||
|
||||
if result.return_code:
|
||||
fail("""bun_install failed running `bun --bun install --frozen-lockfile`.
|
||||
@@ -155,7 +219,7 @@ stderr:
|
||||
"BUILD.bazel",
|
||||
"""filegroup(
|
||||
name = "node_modules",
|
||||
srcs = glob(["node_modules/**"], allow_empty = False),
|
||||
srcs = glob(["**/node_modules/**"], allow_empty = False),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
@@ -166,6 +230,8 @@ bun_install_repository = repository_rule(
|
||||
attrs = {
|
||||
"package_json": attr.label(mandatory = True, allow_single_file = True),
|
||||
"bun_lockfile": attr.label(mandatory = True, allow_single_file = True),
|
||||
"install_inputs": attr.label_list(allow_files = True),
|
||||
"isolated_home": attr.bool(default = True),
|
||||
"bun_linux_x64": attr.label(default = "@bun_linux_x64//:bun-linux-x64/bun", allow_single_file = True),
|
||||
"bun_linux_aarch64": attr.label(default = "@bun_linux_aarch64//:bun-linux-aarch64/bun", allow_single_file = True),
|
||||
"bun_darwin_x64": attr.label(default = "@bun_darwin_x64//:bun-darwin-x64/bun", allow_single_file = True),
|
||||
@@ -174,13 +240,17 @@ bun_install_repository = repository_rule(
|
||||
},
|
||||
)
|
||||
|
||||
def bun_install(name, package_json, bun_lockfile):
|
||||
def bun_install(name, package_json, bun_lockfile, install_inputs = [], isolated_home = True):
|
||||
"""Create an external repository containing installed node_modules.
|
||||
|
||||
Args:
|
||||
name: Repository name to create.
|
||||
package_json: Label to a package.json file.
|
||||
bun_lockfile: Label to a bun.lockb file.
|
||||
install_inputs: Optional additional files under the package root to copy
|
||||
into the install context, such as patch files or auth/config files.
|
||||
isolated_home: Whether to run Bun with HOME set to the generated
|
||||
repository root for a more isolated install context.
|
||||
|
||||
Usage (WORKSPACE):
|
||||
bun_install(
|
||||
@@ -194,4 +264,6 @@ def bun_install(name, package_json, bun_lockfile):
|
||||
name = name,
|
||||
package_json = package_json,
|
||||
bun_lockfile = bun_lockfile,
|
||||
install_inputs = install_inputs,
|
||||
isolated_home = isolated_home,
|
||||
)
|
||||
|
||||
@@ -19,30 +19,115 @@ set -euo pipefail
|
||||
|
||||
runfiles_dir="${{RUNFILES_DIR:-$0.runfiles}}"
|
||||
workspace_root="${{runfiles_dir}}/_main"
|
||||
workspace_root="$(cd "${{workspace_root}}" && pwd -P)"
|
||||
bun_bin="${{runfiles_dir}}/_main/{bun_short_path}"
|
||||
package_json="${{runfiles_dir}}/_main/{package_json_short_path}"
|
||||
package_dir="$(dirname "${{package_json}}")"
|
||||
package_dir="$(cd "$(dirname "${{package_json}}")" && pwd -P)"
|
||||
package_rel_dir="{package_rel_dir}"
|
||||
|
||||
node_modules_bin_dirs=()
|
||||
while IFS= read -r node_modules_bin; do
|
||||
node_modules_bin_dirs+=("${{node_modules_bin}}")
|
||||
done < <(find "${{runfiles_dir}}" -type d -path '*/node_modules/.bin' 2>/dev/null | sort)
|
||||
select_primary_node_modules() {{
|
||||
local selected=""
|
||||
local fallback=""
|
||||
while IFS= read -r node_modules_dir; do
|
||||
if [[ -z "${{fallback}}" ]]; then
|
||||
fallback="${{node_modules_dir}}"
|
||||
fi
|
||||
|
||||
if [[ ${{#node_modules_bin_dirs[@]}} -gt 0 ]]; then
|
||||
export PATH="$(IFS=:; echo "${{node_modules_bin_dirs[*]}}"):${{PATH}}"
|
||||
if [[ ! -d "${{node_modules_dir}}/.bun" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${{node_modules_dir}}" != *"/runfiles/_main/"* ]]; then
|
||||
selected="${{node_modules_dir}}"
|
||||
break
|
||||
fi
|
||||
|
||||
if [[ -z "${{selected}}" ]]; then
|
||||
selected="${{node_modules_dir}}"
|
||||
fi
|
||||
done < <(find -L "${{runfiles_dir}}" -type d -name node_modules 2>/dev/null | sort)
|
||||
|
||||
if [[ -n "${{selected}}" ]]; then
|
||||
echo "${{selected}}"
|
||||
else
|
||||
echo "${{fallback}}"
|
||||
fi
|
||||
}}
|
||||
|
||||
primary_node_modules="$(select_primary_node_modules)"
|
||||
|
||||
runtime_workspace="$(mktemp -d)"
|
||||
cleanup_runtime_workspace() {{
|
||||
rm -rf "${{runtime_workspace}}"
|
||||
}}
|
||||
trap cleanup_runtime_workspace EXIT
|
||||
|
||||
runtime_package_dir="${{runtime_workspace}}/${{package_rel_dir}}"
|
||||
mkdir -p "${{runtime_package_dir}}"
|
||||
cp -RL "${{package_dir}}/." "${{runtime_package_dir}}/"
|
||||
|
||||
install_repo_root=""
|
||||
if [[ -n "${{primary_node_modules}}" ]]; then
|
||||
install_repo_root="$(dirname "${{primary_node_modules}}")"
|
||||
ln -s "${{primary_node_modules}}" "${{runtime_workspace}}/node_modules"
|
||||
fi
|
||||
|
||||
find_node_modules() {{
|
||||
local dir="$1"
|
||||
local root="$2"
|
||||
|
||||
while [[ "$dir" == "$root"* ]]; do
|
||||
if [[ -d "$dir/node_modules" ]]; then
|
||||
echo "$dir/node_modules"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$dir" == "$root" ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
dir="$(dirname "$dir")"
|
||||
done
|
||||
|
||||
return 1
|
||||
}}
|
||||
|
||||
if [[ -n "${{install_repo_root}}" && -d "${{install_repo_root}}/${{package_rel_dir}}/node_modules" ]]; then
|
||||
rm -rf "${{runtime_package_dir}}/node_modules"
|
||||
ln -s "${{install_repo_root}}/${{package_rel_dir}}/node_modules" "${{runtime_package_dir}}/node_modules"
|
||||
else
|
||||
resolved_node_modules="$(find_node_modules "${{runtime_package_dir}}" "${{runtime_workspace}}" || true)"
|
||||
if [[ -n "${{resolved_node_modules}}" && "${{resolved_node_modules}}" != "${{runtime_package_dir}}/node_modules" ]]; then
|
||||
rm -rf "${{runtime_package_dir}}/node_modules"
|
||||
ln -s "${{resolved_node_modules}}" "${{runtime_package_dir}}/node_modules"
|
||||
fi
|
||||
fi
|
||||
|
||||
path_entries=()
|
||||
if [[ -d "${{runtime_package_dir}}/node_modules/.bin" ]]; then
|
||||
path_entries+=("${{runtime_package_dir}}/node_modules/.bin")
|
||||
fi
|
||||
|
||||
if [[ -d "${{runtime_workspace}}/node_modules/.bin" && "${{runtime_workspace}}/node_modules/.bin" != "${{runtime_package_dir}}/node_modules/.bin" ]]; then
|
||||
path_entries+=("${{runtime_workspace}}/node_modules/.bin")
|
||||
fi
|
||||
|
||||
if [[ ${{#path_entries[@]}} -gt 0 ]]; then
|
||||
export PATH="$(IFS=:; echo "${{path_entries[*]}}"):${{PATH}}"
|
||||
fi
|
||||
|
||||
working_dir="{working_dir}"
|
||||
if [[ "${{working_dir}}" == "package" ]]; then
|
||||
cd "${{package_dir}}"
|
||||
cd "${{runtime_package_dir}}"
|
||||
else
|
||||
cd "${{workspace_root}}"
|
||||
cd "${{runtime_workspace}}"
|
||||
fi
|
||||
|
||||
exec "${{bun_bin}}" --bun run {script} "$@"
|
||||
""".format(
|
||||
bun_short_path = bun_bin.short_path,
|
||||
package_json_short_path = package_json.short_path,
|
||||
package_rel_dir = package_json.dirname,
|
||||
working_dir = ctx.attr.working_dir,
|
||||
script = _shell_quote(ctx.attr.script),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user