diff --git a/docs/image.md b/docs/image.md
index 9011d8ab..9b7a03c3 100644
--- a/docs/image.md
+++ b/docs/image.md
@@ -74,8 +74,8 @@ oci_image(
| annotations | A file containing a dictionary of annotations. Each line should be in the form name=value
. | Label | optional | None
|
| architecture | The CPU architecture which the binaries in this image are built to run on. eg: arm64
, arm
, amd64
, s390x
. See $GOARCH documentation for possible values: https://go.dev/doc/install/source#environment | String | optional | ""
|
| base | Label to an oci_image target to use as the base. | Label | optional | None
|
-| cmd | A file containing a comma separated list to be used as the command & args
of the container. These values act as defaults and may be replaced by any specified when creating a container. | Label | optional | None
|
-| entrypoint | A file containing a comma separated list to be used as the entrypoint
to execute when the container starts. These values act as defaults and may be replaced by an entrypoint specified when creating a container. NOTE: Setting this attribute will reset the cmd
attribute | Label | optional | None
|
+| cmd | A file containing a newline separated list to be used as the command & args
of the container. These values act as defaults and may be replaced by any specified when creating a container. | Label | optional | None
|
+| entrypoint | A file containing a newline separated list to be used as the entrypoint
to execute when the container starts. These values act as defaults and may be replaced by an entrypoint specified when creating a container. NOTE: Setting this attribute will reset the cmd
attribute | Label | optional | None
|
| env | A file containing the default values for the environment variables of the container. These values act as defaults and are merged with any specified when creating a container. Entries replace the base environment variables if any of the entries has conflicting keys. To merge entries with keys specified in the base, ${KEY}
or $KEY
syntax may be used. | Label | optional | None
|
| exposed_ports | A file containing a comma separated list of exposed ports. (e.g. 2000/tcp, 3000/udp or 4000. No protocol defaults to tcp). | Label | optional | None
|
| labels | A file containing a dictionary of labels. Each line should be in the form name=value
. | Label | optional | None
|
diff --git a/examples/assertion/BUILD.bazel b/examples/assertion/BUILD.bazel
index 0e43a186..b8135340 100644
--- a/examples/assertion/BUILD.bazel
+++ b/examples/assertion/BUILD.bazel
@@ -519,6 +519,94 @@ assert_oci_config(
image = ":case16",
)
+# Case 17: an entrypoint and cmd with , in it
+oci_image(
+ name = "case17",
+ architecture = "arm64",
+ cmd = [
+ '--permitted-system-keys="MY_HOST_V1,MY_HOST_V2"',
+ ],
+ entrypoint = [
+ "/docker-entrypoint.sh",
+ '--permitted-system-keys="MY_HOST_V1,MY_HOST_V2"',
+ ],
+ os = "linux",
+)
+
+assert_oci_config(
+ name = "test_case17",
+ cmd_eq = [
+ '--permitted-system-keys="MY_HOST_V1,MY_HOST_V2"',
+ ],
+ entrypoint_eq = [
+ "/docker-entrypoint.sh",
+ '--permitted-system-keys="MY_HOST_V1,MY_HOST_V2"',
+ ],
+ image = ":case17",
+)
+
+# Case 18: an entrypoint and with \n in it
+oci_image(
+ name = "case18",
+ architecture = "arm64",
+ cmd = ['--permitted-system-keys="MY_HOST_V1\nMY_HOST_V2"'],
+ entrypoint = ["/docker-entrypoint.sh"],
+ os = "linux",
+)
+
+assert_oci_config(
+ name = "test_case18",
+ cmd_eq = ['--permitted-system-keys="MY_HOST_V1\nMY_HOST_V2"'],
+ entrypoint_eq = ["/docker-entrypoint.sh"],
+ image = ":case18",
+)
+
+# Case 19: an entrypoint with escaped \n in it
+oci_image(
+ name = "case19",
+ architecture = "arm64",
+ entrypoint = [
+ "/docker-entrypoint.sh",
+ '--permitted-system-keys="MY_HOST_V1\\nMY_HOST_V2"',
+ ],
+ os = "linux",
+)
+
+assert_oci_config(
+ name = "test_case19",
+ entrypoint_eq = [
+ "/docker-entrypoint.sh",
+ '--permitted-system-keys="MY_HOST_V1\\nMY_HOST_V2"',
+ ],
+ image = ":case19",
+)
+
+# Case 20: an entrypoint with \t and \n in it
+case20_shell = '''
+#!/usr/bin/env bash
+set -o pipefail -o errexit -o nounset
+string="\t\t\n\n\\n"
+'''
+
+oci_image(
+ name = "case20",
+ architecture = "arm64",
+ entrypoint = [
+ "bash",
+ case20_shell,
+ ],
+ os = "linux",
+)
+
+assert_oci_config(
+ name = "test_case20",
+ entrypoint_eq = [
+ "bash",
+ case20_shell,
+ ],
+ image = ":case20",
+)
+
# build them as test.
build_test(
name = "test",
diff --git a/oci/defs.bzl b/oci/defs.bzl
index 49467bcc..bee35038 100644
--- a/oci/defs.bzl
+++ b/oci/defs.bzl
@@ -22,6 +22,17 @@ oci_image_rule = _oci_image
oci_image_index = _oci_image_index
oci_push_rule = _oci_push
+def _write_nl_seperated_file(name, kind, elems, forwarded_kwargs):
+ label = "_{}_write_{}".format(name, kind)
+ write_file(
+ name = label,
+ out = "_{}.{}.txt".format(name, kind),
+ # %5Cn is the uri escaped newline
+ content = [elem.replace("\n", "%5Cn") for elem in elems],
+ **forwarded_kwargs
+ )
+ return label
+
def oci_image(name, labels = None, annotations = None, env = None, cmd = None, entrypoint = None, exposed_ports = None, volumes = None, **kwargs):
"""Macro wrapper around [oci_image_rule](#oci_image_rule).
@@ -81,24 +92,20 @@ def oci_image(name, labels = None, annotations = None, env = None, cmd = None, e
env = env_label
if types.is_list(cmd):
- cmd_label = "_{}_write_cmd".format(name)
- write_file(
- name = cmd_label,
- out = "_{}.cmd.txt".format(name),
- content = [",".join(cmd)],
- **forwarded_kwargs
+ cmd = _write_nl_seperated_file(
+ name = name,
+ kind = "cmd",
+ elems = cmd,
+ forwarded_kwargs = forwarded_kwargs,
)
- cmd = cmd_label
if types.is_list(entrypoint):
- entrypoint_label = "_{}_write_entrypoint".format(name)
- write_file(
- name = entrypoint_label,
- out = "_{}.entrypoint.txt".format(name),
- content = [",".join(entrypoint)],
- **forwarded_kwargs
+ entrypoint = _write_nl_seperated_file(
+ name = name,
+ kind = "entrypoint",
+ elems = entrypoint,
+ forwarded_kwargs = forwarded_kwargs,
)
- entrypoint = entrypoint_label
if types.is_list(exposed_ports):
exposed_ports_label = "_{}_write_exposed_ports".format(name)
diff --git a/oci/private/image.bzl b/oci/private/image.bzl
index 949427f3..99558e91 100644
--- a/oci/private/image.bzl
+++ b/oci/private/image.bzl
@@ -69,8 +69,8 @@ _attrs = {
The authors recommend [dive](https://github.com/wagoodman/dive) to explore the layering of the resulting image.
"""),
# See: https://github.com/opencontainers/image-spec/blob/main/config.md#properties
- "entrypoint": attr.label(doc = "A file containing a comma separated list to be used as the `entrypoint` to execute when the container starts. These values act as defaults and may be replaced by an entrypoint specified when creating a container. NOTE: Setting this attribute will reset the `cmd` attribute", allow_single_file = True),
- "cmd": attr.label(doc = "A file containing a comma separated list to be used as the `command & args` of the container. These values act as defaults and may be replaced by any specified when creating a container.", allow_single_file = True),
+ "entrypoint": attr.label(doc = "A file containing a newline separated list to be used as the `entrypoint` to execute when the container starts. These values act as defaults and may be replaced by an entrypoint specified when creating a container. NOTE: Setting this attribute will reset the `cmd` attribute", allow_single_file = True),
+ "cmd": attr.label(doc = "A file containing a newline separated list to be used as the `command & args` of the container. These values act as defaults and may be replaced by any specified when creating a container.", allow_single_file = True),
"env": attr.label(doc = """\
A file containing the default values for the environment variables of the container. These values act as defaults and are merged with any specified when creating a container. Entries replace the base environment variables if any of the entries has conflicting keys.
To merge entries with keys specified in the base, `${KEY}` or `$KEY` syntax may be used.
diff --git a/oci/private/image.sh b/oci/private/image.sh
index 78c637f4..13f1a480 100644
--- a/oci/private/image.sh
+++ b/oci/private/image.sh
@@ -146,13 +146,13 @@ for ARG in "$@"; do
CONFIG=$(jq --argjson envs "${env}" '.config.Env = ($envs | map("\(.key)=\(.value)"))' <<<"$CONFIG")
;;
--cmd=*)
- CONFIG=$(jq --rawfile cmd "${ARG#--cmd=}" '.config.Cmd = ($cmd | split(",|\n"; "") | map(select(. | length > 0)))' <<<"$CONFIG")
+ CONFIG=$(jq --rawfile cmd "${ARG#--cmd=}" '.config.Cmd = ($cmd | split("\n") | map(select(. | length > 0)) | map(. | sub("%5Cn"; "\n"; "g")))' <<<"$CONFIG")
;;
--entrypoint=*)
# NOTE: setting entrypoint deletes `.config.Cmd` which is consistent with crane and Dockerfile behavior.
# See: https://github.com/bazel-contrib/rules_oci/issues/649
# See: https://github.com/google/go-containerregistry/blob/c3d1dcc932076c15b65b8b9acfff1d47ded2ebf9/cmd/crane/cmd/mutate.go#L107
- CONFIG=$(jq --rawfile entrypoint "${ARG#--entrypoint=}" '.config.Cmd = null | .config.Entrypoint = ($entrypoint | split(",|\n"; "") | map(select(. | length > 0)))' <<<"$CONFIG")
+ CONFIG=$(jq --rawfile entrypoint "${ARG#--entrypoint=}" '.config.Cmd = null | .config.Entrypoint = ($entrypoint | split("\n") | map(select(. | length > 0)) | map(. | sub("%5Cn"; "\n"; "g")))' <<<"$CONFIG")
;;
--exposed-ports=*)
CONFIG=$(jq --rawfile ep "${ARG#--exposed-ports=}" '.config.ExposedPorts = ($ep | split(",") | map({key: ., value: {}}) | from_entries)' <<<"$CONFIG")