diff --git a/.bazelrc b/.bazelrc index 7bec7cc95..968597053 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,9 +1,14 @@ common --enable_bzlmod +# Use built-in protoc +common --incompatible_enable_proto_toolchain_resolution --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc + build --java_runtime_version=remotejdk_11 build --java_language_version=11 + # Hide Java 8 deprecation warnings. common --javacopt=-Xlint:-options # Remove flag once https://github.com/google/cel-spec/issues/508 and rules_jvm_external is fixed. common --incompatible_autoload_externally=proto_library,cc_proto_library,java_proto_library,java_test + diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 19732cd9e..060b83bdd 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -33,6 +33,8 @@ jobs: disk-cache: ${{ github.workflow }} # Share repository cache between workflows. repository-cache: true + # Never write to the cache, strictly read-only + cache-save: false - name: Unwanted Dependencies run: .github/workflows/unwanted_deps.sh - name: Cross-artifact Duplicate Classes Check @@ -56,6 +58,8 @@ jobs: disk-cache: ${{ github.workflow }} # Share repository cache between workflows. repository-cache: true + # Prevent PRs from polluting cache + cache-save: ${{ github.event_name != 'pull_request' }} - name: Bazel Output Version run: bazelisk --version - name: Java 8 Build @@ -91,12 +95,14 @@ jobs: disk-cache: ${{ github.workflow }} # Share repository cache between workflows. repository-cache: true + # Never write to the cache, strictly read-only + cache-save: false - name: Verify Version Consistency if: steps.changed_file.outputs.any_changed == 'true' run: | CEL_VERSION=$(grep 'CEL_VERSION =' publish/cel_version.bzl | cut -d '"' -f 2) - MODULE_VERSION=$(grep 'dev.cel:cel' MODULE.bazel | cut -d '"' -f 2 | cut -d ':' -f 3) + MODULE_VERSION=$(grep 'CEL_VERSION =' MODULE.bazel | cut -d '"' -f 2) if [ -z "$CEL_VERSION" ] || [ -z "$MODULE_VERSION" ]; then echo "❌ Error: Could not extract one or both version strings." diff --git a/MODULE.bazel b/MODULE.bazel index b3a78627c..007adcb3c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -16,16 +16,16 @@ module( name = "cel_java", ) -bazel_dep(name = "bazel_skylib", version = "1.7.1") +bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "rules_jvm_external", version = "6.9") -bazel_dep(name = "protobuf", version = "29.3", repo_name = "com_google_protobuf") # see https://github.com/bazelbuild/rules_android/issues/373 +bazel_dep(name = "protobuf", version = "33.4", repo_name = "com_google_protobuf") # see https://github.com/bazelbuild/rules_android/issues/373 bazel_dep(name = "googleapis", version = "0.0.0-20241220-5e258e33.bcr.1", repo_name = "com_google_googleapis") bazel_dep(name = "rules_pkg", version = "1.0.1") bazel_dep(name = "rules_license", version = "1.0.0") bazel_dep(name = "rules_proto", version = "7.1.0") -bazel_dep(name = "rules_java", version = "8.12.0") +bazel_dep(name = "rules_java", version = "9.3.0") bazel_dep(name = "rules_android", version = "0.7.1") -bazel_dep(name = "rules_shell", version = "0.5.1") +bazel_dep(name = "rules_shell", version = "0.6.1") bazel_dep(name = "googleapis-java", version = "1.0.0") bazel_dep(name = "cel-spec", version = "0.24.0", repo_name = "cel_spec") @@ -39,7 +39,9 @@ GUAVA_VERSION = "33.5.0" TRUTH_VERSION = "1.4.4" -PROTOBUF_JAVA_VERSION = "4.33.4" +PROTOBUF_JAVA_VERSION = "4.33.5" + +CEL_VERSION = "0.12.0-SNAPSHOT" # Compile only artifacts [ @@ -114,7 +116,11 @@ maven.install( maven.install( name = "maven_conformance", - artifacts = ["dev.cel:cel:0.11.1"], + artifacts = [ + "dev.cel:cel:" + CEL_VERSION, + "dev.cel:compiler:" + CEL_VERSION, + "dev.cel:runtime:" + CEL_VERSION, + ], repositories = [ "https://maven.google.com", "https://repo1.maven.org/maven2", diff --git a/conformance/src/test/java/dev/cel/conformance/BUILD.bazel b/conformance/src/test/java/dev/cel/conformance/BUILD.bazel index 248bc7fe2..ab7468e54 100644 --- a/conformance/src/test/java/dev/cel/conformance/BUILD.bazel +++ b/conformance/src/test/java/dev/cel/conformance/BUILD.bazel @@ -41,7 +41,13 @@ java_library( ], ) -MAVEN_JAR_DEPS = ["@maven_conformance//:dev_cel_cel"] +MAVEN_JAR_DEPS = [ + "@maven_conformance//:dev_cel_compiler", + "@maven_conformance//:dev_cel_common", + "@maven_conformance//:dev_cel_runtime", + "@maven_conformance//:dev_cel_protobuf", + "@maven_conformance//:dev_cel_cel", +] java_library( name = "run_maven_jar", diff --git a/conformance/src/test/java/dev/cel/maven/BUILD.bazel b/conformance/src/test/java/dev/cel/maven/BUILD.bazel new file mode 100644 index 000000000..895339521 --- /dev/null +++ b/conformance/src/test/java/dev/cel/maven/BUILD.bazel @@ -0,0 +1,47 @@ +load("@rules_java//java:defs.bzl", "java_test") + +package(default_applicable_licenses = [ + "//:license", +]) + +# keep sorted +MAVEN_COMPILER_JAR_DEPS = [ + "@maven_conformance//:dev_cel_common", + "@maven_conformance//:dev_cel_compiler", +] + +# keep sorted +MAVEN_RUNTIME_JAR_DEPS = [ + "@maven_conformance//:dev_cel_common", + "@maven_conformance//:dev_cel_protobuf", + "@maven_conformance//:dev_cel_runtime", +] + +java_test( + name = "compiler_artifact_test", + srcs = ["CompilerArtifactTest.java"], + test_class = "dev.cel.maven.CompilerArtifactTest", + deps = + MAVEN_COMPILER_JAR_DEPS + [ + "//:java_truth", + "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto", + "@maven//:com_google_testparameterinjector_test_parameter_injector", + "@maven//:junit_junit", + ], +) + +java_test( + name = "runtime_artifact_test", + srcs = ["RuntimeArtifactTest.java"], + test_class = "dev.cel.maven.RuntimeArtifactTest", + deps = + MAVEN_RUNTIME_JAR_DEPS + [ + "//:java_truth", + "@cel_spec//proto/cel/expr:checked_java_proto", + "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto", + "@maven//:com_google_guava_guava", + "@maven//:com_google_protobuf_protobuf_java", + "@maven//:com_google_testparameterinjector_test_parameter_injector", + "@maven//:junit_junit", + ], +) diff --git a/conformance/src/test/java/dev/cel/maven/CompilerArtifactTest.java b/conformance/src/test/java/dev/cel/maven/CompilerArtifactTest.java new file mode 100644 index 000000000..dcdedb157 --- /dev/null +++ b/conformance/src/test/java/dev/cel/maven/CompilerArtifactTest.java @@ -0,0 +1,106 @@ +// 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. + +package dev.cel.maven; + +import static com.google.common.truth.Truth.assertThat; +import static dev.cel.common.CelFunctionDecl.newFunctionDeclaration; +import static dev.cel.common.CelOverloadDecl.newGlobalOverload; +import static org.junit.Assert.assertThrows; + +import com.google.testing.junit.testparameterinjector.TestParameterInjector; +import dev.cel.checker.CelChecker; +import dev.cel.common.CelAbstractSyntaxTree; +import dev.cel.common.CelContainer; +import dev.cel.common.CelOptions; +import dev.cel.common.CelValidationException; +import dev.cel.common.CelValidationResult; +import dev.cel.common.ast.CelConstant; +import dev.cel.common.ast.CelExpr; +import dev.cel.common.types.ProtoMessageTypeProvider; +import dev.cel.common.types.SimpleType; +import dev.cel.common.types.StructTypeReference; +import dev.cel.compiler.CelCompiler; +import dev.cel.compiler.CelCompilerFactory; +import dev.cel.expr.conformance.proto3.TestAllTypes; +import dev.cel.parser.CelParser; +import dev.cel.parser.CelParserFactory; +import dev.cel.parser.CelStandardMacro; +import dev.cel.parser.CelUnparser; +import dev.cel.parser.CelUnparserFactory; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(TestParameterInjector.class) +public class CompilerArtifactTest { + + @Test + public void parse() throws Exception { + CelParser parser = CelParserFactory.standardCelParserBuilder().build(); + CelUnparser unparser = CelUnparserFactory.newUnparser(); + + CelAbstractSyntaxTree ast = parser.parse("'Hello World'").getAst(); + + assertThat(ast.getExpr()).isEqualTo(CelExpr.ofConstant(1L, CelConstant.ofValue("Hello World"))); + assertThat(unparser.unparse(ast)).isEqualTo("\"Hello World\""); + } + + @Test + public void typeCheck() throws Exception { + CelParser parser = CelParserFactory.standardCelParserBuilder().build(); + CelChecker checker = CelCompilerFactory.standardCelCheckerBuilder().build(); + + CelAbstractSyntaxTree ast = checker.check(parser.parse("'Hello World'").getAst()).getAst(); + + assertThat(ast.getResultType()).isEqualTo(SimpleType.STRING); + } + + @Test + public void compile() throws Exception { + CelCompiler compiler = + CelCompilerFactory.standardCelCompilerBuilder() + .addFunctionDeclarations( + newFunctionDeclaration( + "getThree", newGlobalOverload("getThree_overload", SimpleType.INT))) + .setOptions(CelOptions.DEFAULT) + .setStandardMacros(CelStandardMacro.STANDARD_MACROS) + .setTypeProvider( + ProtoMessageTypeProvider.newBuilder() + .addFileDescriptors(TestAllTypes.getDescriptor().getFile()) + .build()) + .setContainer(CelContainer.ofName("cel.expr.conformance.proto3")) + .addVar("msg", StructTypeReference.create(TestAllTypes.getDescriptor().getFullName())) + .build(); + CelUnparser unparser = CelUnparserFactory.newUnparser(); + + CelAbstractSyntaxTree ast = + compiler.compile("msg == TestAllTypes{} && 3 == getThree()").getAst(); + + assertThat(unparser.unparse(ast)) + .isEqualTo("msg == cel.expr.conformance.proto3.TestAllTypes{} && 3 == getThree()"); + assertThat(ast.getResultType()).isEqualTo(SimpleType.BOOL); + } + + @Test + public void compile_error() { + CelCompiler compiler = CelCompilerFactory.standardCelCompilerBuilder().build(); + + CelValidationResult result = compiler.compile("'foo' + 1"); + + assertThat(result.hasError()).isTrue(); + assertThat(assertThrows(CelValidationException.class, result::getAst)) + .hasMessageThat() + .contains("found no matching overload for '_+_' applied to '(string, int)'"); + } +} diff --git a/conformance/src/test/java/dev/cel/maven/RuntimeArtifactTest.java b/conformance/src/test/java/dev/cel/maven/RuntimeArtifactTest.java new file mode 100644 index 000000000..e129b85d3 --- /dev/null +++ b/conformance/src/test/java/dev/cel/maven/RuntimeArtifactTest.java @@ -0,0 +1,413 @@ +// 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. + +package dev.cel.maven; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import dev.cel.expr.CheckedExpr; +import com.google.common.collect.ImmutableList; +import com.google.protobuf.TextFormat; +import com.google.testing.junit.testparameterinjector.TestParameterInjector; +import dev.cel.common.CelAbstractSyntaxTree; +import dev.cel.common.CelOptions; +import dev.cel.common.CelProtoAbstractSyntaxTree; +import dev.cel.expr.conformance.proto3.TestAllTypes; +import dev.cel.runtime.CelEvaluationException; +import dev.cel.runtime.CelFunctionBinding; +import dev.cel.runtime.CelRuntime; +import dev.cel.runtime.CelRuntimeFactory; +import dev.cel.runtime.CelStandardFunctions; +import dev.cel.runtime.CelStandardFunctions.StandardFunction; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(TestParameterInjector.class) +public class RuntimeArtifactTest { + + // Serialized expr: [TestAllTypes{single_int64: 1}.single_int64, 2].exists(x, x == 2) + private static final String CHECKED_EXPR = + "reference_map {\n" + + " key: 2\n" + + " value {\n" + + " name: \"cel.expr.conformance.proto3.TestAllTypes\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 7\n" + + " value {\n" + + " overload_id: \"getThree_overload\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 10\n" + + " value {\n" + + " name: \"x\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 11\n" + + " value {\n" + + " overload_id: \"greater_equals_int64\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 14\n" + + " value {\n" + + " name: \"@result\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 15\n" + + " value {\n" + + " overload_id: \"not_strictly_false\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 16\n" + + " value {\n" + + " name: \"@result\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 17\n" + + " value {\n" + + " overload_id: \"logical_and\"\n" + + " }\n" + + "}\n" + + "reference_map {\n" + + " key: 18\n" + + " value {\n" + + " name: \"@result\"\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 1\n" + + " value {\n" + + " list_type {\n" + + " elem_type {\n" + + " primitive: INT64\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 2\n" + + " value {\n" + + " message_type: \"cel.expr.conformance.proto3.TestAllTypes\"\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 4\n" + + " value {\n" + + " primitive: INT64\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 5\n" + + " value {\n" + + " primitive: INT64\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 6\n" + + " value {\n" + + " primitive: INT64\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 7\n" + + " value {\n" + + " primitive: INT64\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 10\n" + + " value {\n" + + " primitive: INT64\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 11\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 12\n" + + " value {\n" + + " primitive: INT64\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 13\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 14\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 15\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 16\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 17\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 18\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "type_map {\n" + + " key: 19\n" + + " value {\n" + + " primitive: BOOL\n" + + " }\n" + + "}\n" + + "expr {\n" + + " id: 19\n" + + " comprehension_expr {\n" + + " iter_var: \"x\"\n" + + " iter_range {\n" + + " id: 1\n" + + " list_expr {\n" + + " elements {\n" + + " id: 5\n" + + " select_expr {\n" + + " operand {\n" + + " id: 2\n" + + " struct_expr {\n" + + " message_name: \"cel.expr.conformance.proto3.TestAllTypes\"\n" + + " entries {\n" + + " id: 3\n" + + " field_key: \"single_int64\"\n" + + " value {\n" + + " id: 4\n" + + " const_expr {\n" + + " int64_value: 1\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " field: \"single_int64\"\n" + + " }\n" + + " }\n" + + " elements {\n" + + " id: 6\n" + + " const_expr {\n" + + " int64_value: 2\n" + + " }\n" + + " }\n" + + " elements {\n" + + " id: 7\n" + + " call_expr {\n" + + " function: \"getThree\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " accu_var: \"@result\"\n" + + " accu_init {\n" + + " id: 13\n" + + " const_expr {\n" + + " bool_value: true\n" + + " }\n" + + " }\n" + + " loop_condition {\n" + + " id: 15\n" + + " call_expr {\n" + + " function: \"@not_strictly_false\"\n" + + " args {\n" + + " id: 14\n" + + " ident_expr {\n" + + " name: \"@result\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " loop_step {\n" + + " id: 17\n" + + " call_expr {\n" + + " function: \"_&&_\"\n" + + " args {\n" + + " id: 16\n" + + " ident_expr {\n" + + " name: \"@result\"\n" + + " }\n" + + " }\n" + + " args {\n" + + " id: 11\n" + + " call_expr {\n" + + " function: \"_>=_\"\n" + + " args {\n" + + " id: 10\n" + + " ident_expr {\n" + + " name: \"x\"\n" + + " }\n" + + " }\n" + + " args {\n" + + " id: 12\n" + + " const_expr {\n" + + " int64_value: 0\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " result {\n" + + " id: 18\n" + + " ident_expr {\n" + + " name: \"@result\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n" + + "source_info {\n" + + " location: \"\"\n" + + " line_offsets: 75\n" + + " positions {\n" + + " key: 1\n" + + " value: 0\n" + + " }\n" + + " positions {\n" + + " key: 2\n" + + " value: 13\n" + + " }\n" + + " positions {\n" + + " key: 3\n" + + " value: 26\n" + + " }\n" + + " positions {\n" + + " key: 4\n" + + " value: 28\n" + + " }\n" + + " positions {\n" + + " key: 5\n" + + " value: 30\n" + + " }\n" + + " positions {\n" + + " key: 6\n" + + " value: 45\n" + + " }\n" + + " positions {\n" + + " key: 7\n" + + " value: 56\n" + + " }\n" + + " positions {\n" + + " key: 9\n" + + " value: 64\n" + + " }\n" + + " positions {\n" + + " key: 10\n" + + " value: 67\n" + + " }\n" + + " positions {\n" + + " key: 11\n" + + " value: 69\n" + + " }\n" + + " positions {\n" + + " key: 12\n" + + " value: 72\n" + + " }\n" + + " positions {\n" + + " key: 13\n" + + " value: 63\n" + + " }\n" + + " positions {\n" + + " key: 14\n" + + " value: 63\n" + + " }\n" + + " positions {\n" + + " key: 15\n" + + " value: 63\n" + + " }\n" + + " positions {\n" + + " key: 16\n" + + " value: 63\n" + + " }\n" + + " positions {\n" + + " key: 17\n" + + " value: 63\n" + + " }\n" + + " positions {\n" + + " key: 18\n" + + " value: 63\n" + + " }\n" + + " positions {\n" + + " key: 19\n" + + " value: 63\n" + + " }\n" + + "}\n"; + + @Test + public void eval() throws Exception { + CelRuntime runtime = + CelRuntimeFactory.standardCelRuntimeBuilder() + .setOptions(CelOptions.DEFAULT) + .setStandardEnvironmentEnabled(false) + .addFunctionBindings( + CelFunctionBinding.fromOverloads( + "getThree", + CelFunctionBinding.from("getThree_overload", ImmutableList.of(), arg -> 3L))) + .setStandardFunctions( + CelStandardFunctions.newBuilder() + .includeFunctions( + StandardFunction.GREATER_EQUALS, + StandardFunction.LOGICAL_NOT, + StandardFunction.NOT_STRICTLY_FALSE) + .build()) + .addMessageTypes(TestAllTypes.getDescriptor()) + .build(); + CheckedExpr checkedExpr = TextFormat.parse(CHECKED_EXPR, CheckedExpr.class); + CelAbstractSyntaxTree ast = CelProtoAbstractSyntaxTree.fromCheckedExpr(checkedExpr).getAst(); + + boolean result = (boolean) runtime.createProgram(ast).eval(); + + assertThat(result).isTrue(); + } + + @Test + public void eval_error() throws Exception { + CelRuntime runtime = + CelRuntimeFactory.standardCelRuntimeBuilder() + .addMessageTypes(TestAllTypes.getDescriptor()) + .build(); + CheckedExpr checkedExpr = TextFormat.parse(CHECKED_EXPR, CheckedExpr.class); + CelAbstractSyntaxTree ast = CelProtoAbstractSyntaxTree.fromCheckedExpr(checkedExpr).getAst(); + + assertThat(assertThrows(CelEvaluationException.class, () -> runtime.createProgram(ast).eval())) + .hasMessageThat() + .contains("No matching overload for function 'getThree'"); + } +} diff --git a/publish/cel_version.bzl b/publish/cel_version.bzl index 7938f15a1..ea793eee5 100644 --- a/publish/cel_version.bzl +++ b/publish/cel_version.bzl @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. """Maven artifact version for CEL.""" -CEL_VERSION = "0.11.1" +CEL_VERSION = "0.12.0-SNAPSHOT"