Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8.7.0
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ module(
version = "0.1.0",
)

bazel_dep(name = "rules_python", version = "1.9.1")
bazel_dep(name = "rules_license", version = "1.0.0")
bazel_dep(name = "cel-spec", version = "0.25.1", repo_name = "cel_spec")
37 changes: 37 additions & 0 deletions bazel/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

load("@rules_python//python:py_binary.bzl", "py_binary")
load("@rules_python//python:py_library.bzl", "py_library")
load("@rules_python//python:py_test.bzl", "py_test")
load(":test_bundle.bzl", "cel_policy_test_bundle")

package(
default_applicable_licenses = ["//:license"],
default_visibility = ["//:__subpackages__"],
)

exports_files(["test_bundle.bzl"])

py_library(
name = "bundle_lib",
srcs = ["bundle.py"],
)

py_binary(
name = "bundle",
srcs = ["bundle.py"],
visibility = ["//visibility:public"],
deps = [":bundle_lib"],
)
85 changes: 85 additions & 0 deletions bazel/bundle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
r"""Script to bundle CEL policy conformance tests.

Each CEL policy conformance test is a self-contained directory containing an
optional environment, a policy, and a test suite. This script
bundles them all together into a single multidocument YAML file to simplify file
loading.

Intended to be called via bazel rule.

Format:

```
# config.yaml
{environment}
---
# policy.yaml
{policy}
---
# tests.yaml
{test}
```

Usage:
bazel run //third_party/cel/policy/bazel:bundle -- \
--environment=<environment file> \
--policy=<policy file> \
--test=<test file> \
--output=<path to output file>
"""

import argparse
from collections.abc import Sequence
import os
import sys


def _read_and_format_section(path: str) -> bytes:
with open(path, "rb") as f:
content = f.read().rstrip()
comment = f"# {os.path.basename(path)}".encode("utf-8")
return comment + b"\n" + content


def bundle(environment: str | None, policy: str, test: str) -> bytes:
sections = []
if environment:
sections.append(_read_and_format_section(environment))
sections.append(_read_and_format_section(policy))
sections.append(_read_and_format_section(test))
return b"\n---\n".join(sections) + b"\n"


def main(argv: Sequence[str]) -> None:
parser = argparse.ArgumentParser(
description="Bundle CEL policy conformance tests."
)
parser.add_argument(
"--environment",
help="Path to the environment file.",
required=False,
default=None,
)
parser.add_argument(
"--policy",
help="Path to the policy file.",
required=True,
)
parser.add_argument(
"--test",
help="Path to the test file.",
required=True,
)
parser.add_argument(
"--output",
help="Path to the output file.",
required=True,
)
args = parser.parse_args(argv[1:])
bundled_content = bundle(args.environment, args.policy, args.test)
with open(args.output, "wb") as f:
f.write(bundled_content)


if __name__ == "__main__":
main(sys.argv)
62 changes: 62 additions & 0 deletions bazel/test_bundle.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Defines a bazel rule for bundling CEL policy conformance tests."""

def _cel_policy_test_bundle_impl(ctx):
inputs = []
args = ctx.actions.args()
if ctx.file.environment:
inputs.append(ctx.file.environment)
args.add("--environment", ctx.file.environment.path)
if ctx.file.policy:
inputs.append(ctx.file.policy)
args.add("--policy", ctx.file.policy.path)
if ctx.file.test:
inputs.append(ctx.file.test)
args.add("--test", ctx.file.test.path)

args.add("--output", ctx.outputs.out.path)

ctx.actions.run(
inputs = inputs,
outputs = [ctx.outputs.out],
executable = ctx.executable._bundle_tool,
arguments = [args],
mnemonic = "CelPolicyBundle",
progress_message = "Bundling CEL policy test %s" % ctx.label,
)
return [DefaultInfo(files = depset([ctx.outputs.out]))]

cel_policy_test_bundle = rule(
doc = """Bundles CEL policy conformance tests into a single YAML file.

Output file is a YAML file with three documents: environment, policy, and
test.

If there is no environment file, the environment document will be omitted.
""",
implementation = _cel_policy_test_bundle_impl,
outputs = {"out": "%{name}_bundle.yaml"},
attrs = {
"environment": attr.label(allow_single_file = [".yaml"]),
"policy": attr.label(allow_single_file = [".yaml"], mandatory = True),
"test": attr.label(allow_single_file = [".yaml"], mandatory = True),
"_bundle_tool": attr.label(
default = "//bazel:bundle",
executable = True,
cfg = "exec",
),
},
)
67 changes: 67 additions & 0 deletions conformance/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
load("//bazel:test_bundle.bzl", "cel_policy_test_bundle")

package(
default_applicable_licenses = ["//:license"],
default_testonly = True,
Expand All @@ -20,3 +22,68 @@ exports_files(
"//visibility:public",
],
)

_TEST_DIRS = [
"context_pb",
"k8s",
"limits",
"nested_rule",
"nested_rule2",
"nested_rule3",
"nested_rule4",
"nested_rule5",
"nested_rule6",
"nested_rule7",
"nested_rules_variable_shadowing",
"pb",
"required_labels",
"restricted_destinations",
"unconditional_rules",
"unnest",
"variable_type_propagation",
]

_TEST_ERROR_DIRS_WITH_ENV = [
"compose_conflicting_output",
"compose_conflicting_subrule",
"unreachable",
]

_TEST_ERROR_DIRS = [
"duplicate_variable",
"import",
"incompatible_outputs",
"syntax",
"undeclared_reference",
]

[cel_policy_test_bundle(
name = dir_name,
environment = "testdata/%s/config.yaml" % dir_name,
policy = "testdata/%s/policy.yaml" % dir_name,
test = "testdata/%s/tests.yaml" % dir_name,
) for dir_name in _TEST_DIRS]

[cel_policy_test_bundle(
name = "compile_error_" + dir_name,
environment = "testdata/compile_errors/%s/config.yaml" % dir_name,
policy = "testdata/compile_errors/%s/policy.yaml" % dir_name,
test = "testdata/compile_errors/%s/tests.yaml" % dir_name,
) for dir_name in _TEST_ERROR_DIRS_WITH_ENV]

[cel_policy_test_bundle(
name = "compile_error_" + dir_name,
policy = "testdata/compile_errors/%s/policy.yaml" % dir_name,
test = "testdata/compile_errors/%s/tests.yaml" % dir_name,
) for dir_name in _TEST_ERROR_DIRS]

filegroup(
name = "test_bundles",
srcs = [
":%s" % dir_name
for dir_name in _TEST_DIRS
] + [
":compile_error_" + dir_name
for dir_name in _TEST_ERROR_DIRS_WITH_ENV + _TEST_ERROR_DIRS
],
)
Loading