diff --git a/docs/image.md b/docs/image.md index a04b9e60..6bdc2649 100644 --- a/docs/image.md +++ b/docs/image.md @@ -112,10 +112,14 @@ Can by configured using either dict(key->value) or a file that contains key=v deterministic) information when running with `--stamp` flag. See the example in [/examples/labels/BUILD.bazel](https://github.com/bazel-contrib/rules_oci/blob/main/examples/labels/BUILD.bazel). -**LIST_OR_LABEL**: `cmd`, `entrypoint`, `workdir`, `exposed_ports`, `volumes` +**LIST_OR_LABEL**: `cmd`, `entrypoint`, `exposed_ports`, `volumes` Can be a list of strings, or a file with newlines separating entries. +**STRING_OR_LABEL**: `workdir` + +A string, or a target text file whose output contains a single line + **PARAMETERS** @@ -128,7 +132,7 @@ Can be a list of strings, or a file with newlines separating entries. | env | DICT_OR_LABEL Environment variables provisioned by default to the running container. | None | | cmd | LIST_OR_LABEL Command & argument configured by default in the running container. | None | | entrypoint | LIST_OR_LABEL Entrypoint configured by default in the running container. | None | -| workdir | LIST_OR_LABEL Workdir configured by default in the running container. Only 1 list entry allowed. | None | +| workdir | STRING_OR_LABEL Workdir configured by default in the running container. | None | | exposed_ports | LIST_OR_LABEL Exposed ports in the running container. | None | | volumes | LIST_OR_LABEL Volumes for the container. | None | | kwargs | other named arguments to [oci_image_rule](#oci_image_rule) and [common rule attributes](https://bazel.build/reference/be/common-definitions#common-attributes). | none | diff --git a/examples/assertion/BUILD.bazel b/examples/assertion/BUILD.bazel index 72dbf299..fde1bddf 100644 --- a/examples/assertion/BUILD.bazel +++ b/examples/assertion/BUILD.bazel @@ -190,7 +190,7 @@ oci_image( volumes = ["/srv/data"], # user & workdir user = "root", - workdir = ["/root"], + workdir = "/root", # labels & annotations labels = { "org.opencontainers.image.version": "0.0.0", @@ -620,7 +620,7 @@ build_test( ], ) -# Case 21: accept workdir param as a target (list input tested in case8) +# Case 21: accept workdir param as a target label (string input is tested in case8) genrule( name = "case21_workdir_target", outs = ["case21_workdir_target.txt"], @@ -630,7 +630,7 @@ genrule( oci_image( name = "case21", base = ":case8", - workdir = ":case21_workdir_target", + workdir = "case21_workdir_target", ) assert_oci_config( diff --git a/oci/defs.bzl b/oci/defs.bzl index f6f0c1dd..5a8093d2 100644 --- a/oci/defs.bzl +++ b/oci/defs.bzl @@ -33,6 +33,38 @@ def _write_nl_seperated_file(name, kind, elems, forwarded_kwargs): ) return label + +# There's no "is this a label or just a string?" check in macro land, so +# approximate it with some silly string heuristics. See +# https://bazel.build/concepts/labels for label name rules +def _is_a_workdir_label(input): + if input[0] in (":", "@") or input[:2] in ("@@", "//") or '/' not in input: + return True + return False + + +def test_is_a_workdir_label(): + testdata = { + # Corner case: "foo" could be either a string or a target. This + # implementation chooses target. If you want a relative workdir "foo" + # use "./foo" + "foo": True, + "//foo": True, + "@@foo//bar": True, + ":foo": True, + # These are all not labels + "/foo": False, + "./foo": False, + "foo/bar": False, + "../foo": False, + } + + for input, expected in testdata.items(): + value = _is_a_workdir_label(input) + if value != expected: + fail("_is_a_workdir_label(%s) returned %s, expected %s" % (input, value, expected)) + + def oci_image(name, labels = None, annotations = None, env = None, cmd = None, entrypoint = None, workdir = None, exposed_ports = None, volumes = None, **kwargs): """Macro wrapper around [oci_image_rule](#oci_image_rule). @@ -50,10 +82,14 @@ def oci_image(name, labels = None, annotations = None, env = None, cmd = None, e deterministic) information when running with `--stamp` flag. See the example in [/examples/labels/BUILD.bazel](https://github.com/bazel-contrib/rules_oci/blob/main/examples/labels/BUILD.bazel). - **LIST_OR_LABEL**: `cmd`, `entrypoint`, `workdir`, `exposed_ports`, `volumes` + **LIST_OR_LABEL**: `cmd`, `entrypoint`, `exposed_ports`, `volumes` Can be a list of strings, or a file with newlines separating entries. + **STRING_OR_LABEL**: `workdir` + + A string, or a target text file whose output contains a single line + Args: name: name of resulting oci_image_rule labels: `DICT_OR_LABEL` Labels for the image config. @@ -61,7 +97,7 @@ def oci_image(name, labels = None, annotations = None, env = None, cmd = None, e env: `DICT_OR_LABEL` Environment variables provisioned by default to the running container. cmd: `LIST_OR_LABEL` Command & argument configured by default in the running container. entrypoint: `LIST_OR_LABEL` Entrypoint configured by default in the running container. - workdir: `LIST_OR_LABEL` Workdir configured by default in the running container. Only 1 list entry allowed. + workdir: `STRING_OR_LABEL` Workdir configured by default in the running container. exposed_ports: `LIST_OR_LABEL` Exposed ports in the running container. volumes: `LIST_OR_LABEL` Volumes for the container. **kwargs: other named arguments to [oci_image_rule](#oci_image_rule) and @@ -115,15 +151,13 @@ def oci_image(name, labels = None, annotations = None, env = None, cmd = None, e forwarded_kwargs = forwarded_kwargs, ) - if types.is_list(workdir): - if len(workdir) > 1: - fail("workdir MUST only include 1 list element") - + # Support a string for convenience. Create a label on the fly. + if workdir != None and not _is_a_workdir_label(workdir): workdir_label = "_{}_write_workdir".format(name) write_file( name = workdir_label, out = "_{}.workdir.txt".format(name), - content = workdir, + content = [workdir], **forwarded_kwargs ) workdir = workdir_label diff --git a/oci/tests/BUILD.bazel b/oci/tests/BUILD.bazel index 14819d04..2c558ed3 100644 --- a/oci/tests/BUILD.bazel +++ b/oci/tests/BUILD.bazel @@ -2,6 +2,7 @@ load("@aspect_bazel_lib//lib:diff_test.bzl", "diff_test") load("@aspect_bazel_lib//lib:run_binary.bzl", "run_binary") load("@bazel_skylib//rules:build_test.bzl", "build_test") load(":pull_tests.bzl", "parse_image_test") +load("//oci:defs.bzl", "test_is_a_workdir_label") IMAGES_TO_TEST = { "linux/amd64": { @@ -83,3 +84,5 @@ build_test( ) parse_image_test(name = "parse_image_test") + +test_is_a_workdir_label()