Skip to content

Commit 7d28f8e

Browse files
authored
fix: correctly handle empty chdir on js_binary (#2815)
The `js_binary` rule has a `chdir` attribute, and it is common to set `chdir = package_name()` to indicate that the binary should run under the target bin directory in the subdirectory corresponding to the current package. The logic for this checks `if ctx.attr.chdir`, which is usually fine but does not work correctly when the target is at the top level of an external repo. In that case `package_name()` is empty, making `chdir` falsy, but we do still need to change directories since the package is in an external repo. This change fixes that bug and adds a test verifying that the binary runs in the expected working directory. --- ### Changes are visible to end-users: yes - Searched for relevant documentation and updated as needed: yes - Breaking change (forces users to change their own code or config): no - Suggested release notes appear below: yes The js_binary rule now correctly handles an empty chdir in a target in an external repo. ### Test plan - Covered by existing test cases - New test cases added
1 parent bde0ec5 commit 7d28f8e

7 files changed

Lines changed: 86 additions & 1 deletion

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
workspace/

e2e/js_binary_workspace/BUILD.bazel

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,13 @@ build_test(
1414
"@workspace//:run_bin",
1515
],
1616
)
17+
18+
# Verify that js_binary and js_test in an external repo with chdir =
19+
# package_name() run in the expected working directory.
20+
test_suite(
21+
name = "external_tests",
22+
tests = [
23+
"@workspace//:js_binary_chdir_test",
24+
"@workspace//:js_test_chdir_test",
25+
],
26+
)

e2e/js_binary_workspace/MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module(name = "js_binary_workspace")
22

33
bazel_dep(name = "aspect_rules_js", version = "0.0.0", dev_dependency = True)
44
bazel_dep(name = "bazel_skylib", version = "1.8.1", dev_dependency = True)
5+
bazel_dep(name = "rules_shell", version = "0.6.1", dev_dependency = True)
56

67
local_path_override(
78
module_name = "aspect_rules_js",

e2e/js_binary_workspace/workspace/BUILD.bazel

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_run_binary")
1+
load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_run_binary", "js_test")
2+
load("@bazel_skylib//rules:write_file.bzl", "write_file")
3+
load("@rules_shell//shell:sh_test.bzl", "sh_test")
24

35
js_binary(
46
name = "bin",
@@ -13,3 +15,48 @@ js_run_binary(
1315
tool = ":bin",
1416
visibility = ["//visibility:public"],
1517
)
18+
19+
# Write the expected relative working directory beginning with bazel-out/
20+
genrule(
21+
name = "working_directory_expected",
22+
outs = ["working_directory_expected.txt"],
23+
cmd = "echo $(@D) > $@",
24+
)
25+
26+
# Check that the working directory used by js_test matches the chdir argument.
27+
js_test(
28+
name = "js_test_chdir_test",
29+
args = ["$(rlocationpath :working_directory_expected.txt)"],
30+
chdir = package_name(),
31+
data = [":working_directory_expected.txt"],
32+
entry_point = "check_chdir.mjs",
33+
tags = ["manual"],
34+
)
35+
36+
write_file(
37+
name = "working_directory_test_sh",
38+
out = "working_directory_test.sh",
39+
content = [
40+
"#!/usr/bin/env bash",
41+
"actual=$1",
42+
"expected=$2",
43+
'expected_len=$(wc -c < "$expected")',
44+
# Assert that the end of $actual exactly matches $expected
45+
'diff -u <(tail -c "$expected_len" "$actual") "$expected"',
46+
],
47+
)
48+
49+
# Check that the working directory used by js_run_binary matches the chdir
50+
# argument on js_binary.
51+
sh_test(
52+
name = "js_binary_chdir_test",
53+
srcs = ["working_directory_test.sh"],
54+
args = [
55+
"$(rootpath :out_workspace.txt)",
56+
"$(rootpath :working_directory_expected.txt)",
57+
],
58+
data = [
59+
":out_workspace.txt",
60+
":working_directory_expected.txt",
61+
],
62+
)

e2e/js_binary_workspace/workspace/MODULE.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
module(name = "workspace")
22

33
bazel_dep(name = "aspect_rules_js", version = "0.0.0")
4+
bazel_dep(name = "bazel_skylib", version = "1.5.0")
5+
bazel_dep(name = "rules_shell", version = "0.6.1")
6+
47
local_path_override(
58
module_name = "aspect_rules_js",
69
path = "../../..",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { readFileSync } from 'fs';
2+
import { join } from 'path';
3+
4+
const runfiles = process.env.JS_BINARY__RUNFILES;
5+
const expectedFile = join(runfiles, process.argv[2]);
6+
const expected = readFileSync(expectedFile, 'utf8').trim();
7+
const cwd = process.cwd();
8+
9+
if (!cwd.endsWith(expected)) {
10+
process.stderr.write(`Expected cwd to end with:\n ${expected}\nActual cwd:\n ${cwd}\n`);
11+
process.exit(1);
12+
}

js/defs.bzl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ def js_binary(**kwargs):
8383
Args:
8484
**kwargs: All attributes of the [js_binary](#js_binary) rule.
8585
"""
86+
87+
# Often a js_binary target will set "chdir = package_name()", and if it is
88+
# in the top-level directory then this will result in an empty string. That
89+
# argument may still be significant, though, particularly if the target is
90+
# in an external repo. We make sure to replace an empty string with "." so
91+
# that the underlying rule can distinguish this from an unset chdir
92+
# parameter.
93+
if kwargs.get("chdir") == "":
94+
kwargs["chdir"] = "."
8695
_js_binary(
8796
enable_runfiles = select({
8897
Label("@bazel_lib//lib:enable_runfiles"): True,
@@ -117,6 +126,8 @@ def js_test(**kwargs):
117126
Args:
118127
**kwargs: All attributes of the [js_test](#js_test) rule.
119128
"""
129+
if kwargs.get("chdir") == "":
130+
kwargs["chdir"] = "."
120131
_js_test(
121132
enable_runfiles = select({
122133
Label("@bazel_lib//lib:enable_runfiles"): True,

0 commit comments

Comments
 (0)