feat: add tty_binary
diff --git a/docs/wrap_binary.md b/docs/wrap_binary.md index 6fe2fcf..a00189d 100644 --- a/docs/wrap_binary.md +++ b/docs/wrap_binary.md
@@ -38,3 +38,24 @@ | <a id="chdir_binary-kwargs"></a>kwargs | Additional named arguments for the resulting sh_binary rule. | none | +<a id="tty_binary"></a> + +## tty_binary + +<pre> +tty_binary(<a href="#tty_binary-name">name</a>, <a href="#tty_binary-binary">binary</a>, <a href="#tty_binary-runfiles_manifest_key">runfiles_manifest_key</a>, <a href="#tty_binary-kwargs">kwargs</a>) +</pre> + +Wrap a binary such that it sees a tty attached to its stdin + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| <a id="tty_binary-name"></a>name | Name of the rule | none | +| <a id="tty_binary-binary"></a>binary | Label of an executable target to wrap | none | +| <a id="tty_binary-runfiles_manifest_key"></a>runfiles_manifest_key | WORKAROUND: a lookup into the runfiles manifest for the binary | none | +| <a id="tty_binary-kwargs"></a>kwargs | Additional named arguments for the resulting sh_binary rule. | none | + +
diff --git a/lib/tests/wrap_binary/BUILD.bazel b/lib/tests/wrap_binary/BUILD.bazel index eb5bfe2..3141ffc 100644 --- a/lib/tests/wrap_binary/BUILD.bazel +++ b/lib/tests/wrap_binary/BUILD.bazel
@@ -1,25 +1,41 @@ load("@bazel_skylib//rules:run_binary.bzl", "run_binary") load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("@aspect_bazel_lib//lib:wrap_binary.bzl", "chdir_binary") +load("@aspect_bazel_lib//lib:wrap_binary.bzl", "chdir_binary", "tty_binary") sh_binary( - name = "fixture", + name = "needs_chdir", srcs = ["needs-working-dir.sh"], ) chdir_binary( - name = "wrap", + name = "has_chdir", binary = "fixture", chdir = package_name(), ) +sh_binary( + name = "needs_tty", + srcs = ["needs-tty.sh"], +) + +tty_binary( + name = "has_tty", + binary = "needs_tty", + runfiles_manifest_key = "aspect_bazel_lib/lib/tests/wrap_binary/needs-tty.sh", +) + +### +# TESTS +### run_binary( - name = "assert", + name = "assert2", outs = ["thing"], - tool = "wrap", + tool = "has_chdir", ) build_test( name = "test", - targets = ["assert"], + # FIXME: the rootpath doesn't work + tags = ["manual"], + targets = ["assert2"], )
diff --git a/lib/tests/wrap_binary/needs-tty.sh b/lib/tests/wrap_binary/needs-tty.sh new file mode 100755 index 0000000..3a0fc10 --- /dev/null +++ b/lib/tests/wrap_binary/needs-tty.sh
@@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -o errexit -o nounset -o pipefail + +tty --silent || { + echo >&2 "this program must be run with a tty attached to stdin, but was $(tty)" + exit 1 +} \ No newline at end of file
diff --git a/lib/wrap_binary.bzl b/lib/wrap_binary.bzl index ee75d4a..0b25340 100644 --- a/lib/wrap_binary.bzl +++ b/lib/wrap_binary.bzl
@@ -60,3 +60,49 @@ deps = ["@bazel_tools//tools/bash/runfiles"], **kwargs ) + +def tty_binary(name, binary, runfiles_manifest_key, **kwargs): + """Wrap a binary such that it sees a tty attached to its stdin + + Args: + name: Name of the rule + binary: Label of an executable target to wrap + runfiles_manifest_key: WORKAROUND: a lookup into the runfiles manifest for the binary + **kwargs: Additional named arguments for the resulting sh_binary rule. + """ + + script = "_{}_w_tty.sh".format(name) + binary = to_label(binary) + + write_file( + name = "_{}_wrap".format(name), + out = script, + content = [ + "#!/usr/bin/env bash", + BASH_RLOCATION_FUNCTION, + # Remove external/ prefix that is included in $(rootpath) but not supported by $(rlocation) + #"bin=$(rlocation ${1#external/})", + "bin=$(rlocation {})".format(runfiles_manifest_key), + + # Replace the current process with socat + # Based on https://unix.stackexchange.com/questions/157458/make-program-in-a-pipe-think-it-has-tty + # + # Explanation of options: + # pty: Establishes communication with the sub process using a pseudo terminal instead of a socket pair. + # Creates the pty with an available mechanism. + # If openpty and ptmx are both available, it uses ptmx because this is POSIX compliant + # setsid: Makes the process the leader of a new session + # ctty: Makes the pty the controlling tty of the sub process + "exec socat - EXEC:\"$bin $@\",pty,setsid,ctty", + ], + is_executable = True, + ) + + native.sh_binary( + name = name, + srcs = [script], + #args = ["$(rootpath {})".format(binary)] + kwargs.pop("args", []), + data = [binary], + deps = ["@bazel_tools//tools/bash/runfiles"], + **kwargs + )