Skip to content

Commit

Permalink
tooling: generate JSON schemas from protobuf definitions (envoyproxy#…
Browse files Browse the repository at this point in the history
…27640)

Signed-off-by: norbjd <[email protected]>
  • Loading branch information
norbjd authored Feb 26, 2024
1 parent 0ff8260 commit e6e5e9a
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 8 deletions.
3 changes: 3 additions & 0 deletions api/bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def api_dependencies():
external_http_archive(
name = "com_github_chrusty_protoc_gen_jsonschema",
)
external_http_archive(
name = "rules_proto_grpc",
)

external_http_archive(
name = "envoy_toolshed",
Expand Down
11 changes: 11 additions & 0 deletions api/bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,17 @@ REPOSITORY_LOCATIONS_SPEC = dict(
use_category = ["build"],
release_date = "2023-05-30",
),
rules_proto_grpc = dict(
project_name = "rules_proto_grpc",
project_desc = "Bazel rules for building Protobuf and gRPC code and libraries from proto_library targets ",
project_url = "https://github.com/rules-proto-grpc/rules_proto_grpc",
version = "4.4.0",
sha256 = "928e4205f701b7798ce32f3d2171c1918b363e9a600390a25c876f075f1efc0a",
strip_prefix = "rules_proto_grpc-{version}",
urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/releases/download/{version}/rules_proto_grpc-{version}.tar.gz"],
use_category = ["build"],
release_date = "2023-05-03",
),
envoy_toolshed = dict(
project_name = "envoy_toolshed",
project_desc = "Tooling, libraries, runners and checkers for Envoy proxy's CI",
Expand Down
2 changes: 2 additions & 0 deletions bazel/dependency_imports.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ load("@com_github_aignas_rules_shellcheck//:deps.bzl", "shellcheck_dependencies"
load("@aspect_bazel_lib//lib:repositories.bzl", "register_jq_toolchains", "register_yq_toolchains")
load("@com_google_cel_cpp//bazel:deps.bzl", "parser_deps")
load("@com_github_chrusty_protoc_gen_jsonschema//:deps.bzl", protoc_gen_jsonschema_go_dependencies = "go_dependencies")
load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains")

# go version for rules_go
GO_VERSION = "1.20"
Expand Down Expand Up @@ -156,6 +157,7 @@ def envoy_dependency_imports(go_version = GO_VERSION, jq_version = JQ_VERSION, y
)

protoc_gen_jsonschema_go_dependencies()
rules_proto_grpc_toolchains()

def envoy_download_go_sdks(go_version):
go_download_sdk(
Expand Down
24 changes: 16 additions & 8 deletions tools/api_proto_plugin/plugin.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def _path_ignoring_repository(f):
def _input_arg(i):
return "%s=%s" % (i.basename, i.path)

def api_proto_plugin_impl(target, ctx, output_group, mnemonic, output_suffixes):
def api_proto_plugin_impl(target, ctx, output_group, mnemonic, output_suffixes, output_dir = ""):
# Compute output files from the current proto_library node's dependencies.
transitive_outputs = depset(transitive = [dep.output_groups[output_group] for dep in ctx.rule.attr.deps])

Expand Down Expand Up @@ -53,18 +53,26 @@ def api_proto_plugin_impl(target, ctx, output_group, mnemonic, output_suffixes):
for f in target[ProtoInfo].transitive_sources.to_list():
import_paths.append("{}={}".format(_path_ignoring_repository(f), f.path))

# The outputs live in the ctx.label's package root. We add some additional
# path information to match with protoc's notion of path relative locations.
outputs = []
for output_suffix in output_suffixes:
outputs += [ctx.actions.declare_file(ctx.label.name + "/" + _path_ignoring_repository(f) +
output_suffix) for f in proto_sources]
output_path = ""

if output_suffixes:
# The outputs live in the ctx.label's package root. We add some additional
# path information to match with protoc's notion of path relative locations.
outputs = []
for output_suffix in output_suffixes:
outputs += [ctx.actions.declare_file(ctx.label.name + "/" + _path_ignoring_repository(f) +
output_suffix) for f in proto_sources]

ctx_path = ctx.label.package + "/" + ctx.label.name
output_path = outputs[0].root.path + "/" + outputs[0].owner.workspace_root + "/" + ctx_path
elif output_dir:
outputs.append(ctx.actions.declare_directory(output_dir))
output_path = outputs[0].path

# Create the protoc command-line args.
inputs = [target[ProtoInfo].transitive_sources]

ctx_path = ctx.label.package + "/" + ctx.label.name
output_path = outputs[0].root.path + "/" + outputs[0].owner.workspace_root + "/" + ctx_path
args = ctx.actions.args()
args.add(ctx.label.workspace_root, format = "-I./%s")
args.add_all(import_paths, format_each = "-I%s")
Expand Down
22 changes: 22 additions & 0 deletions tools/protojsonschema/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
load("@rules_proto_grpc//:defs.bzl", "proto_plugin")
load(":generate.bzl", "jsonschema_compile")

licenses(["notice"]) # Apache 2

proto_plugin(
name = "protoc_gen_jsonschema_proto_plugin",
output_directory = True,
tool = "@com_github_chrusty_protoc_gen_jsonschema//cmd/protoc-gen-jsonschema",
)

[
jsonschema_compile(
# example: "@envoy_api//envoy/config/bootstrap/v3:pkg" => "envoy_config_bootstrap_v3"
name = proto.replace("@envoy_api//", "").replace("/", "_").replace(":pkg", ""),
protos = [proto],
)
for proto in [
"@envoy_api//envoy/config/bootstrap/v2:pkg",
"@envoy_api//envoy/config/bootstrap/v3:pkg",
]
]
22 changes: 22 additions & 0 deletions tools/protojsonschema/generate.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
load(
"@rules_proto_grpc//:defs.bzl",
"ProtoPluginInfo",
"proto_compile_attrs",
"proto_compile_impl",
)

# Create compile rule
jsonschema_compile = rule(
implementation = proto_compile_impl,
attrs = dict(
proto_compile_attrs,
_plugins = attr.label_list(
providers = [ProtoPluginInfo],
default = [
Label(":protoc_gen_jsonschema_proto_plugin"),
],
doc = "List of protoc plugins to apply",
),
),
toolchains = [str(Label("@rules_proto_grpc//protobuf:toolchain_type"))],
)
12 changes: 12 additions & 0 deletions tools/protojsonschema_with_aspects/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("//tools/protojsonschema_with_aspects:protojsonschema.bzl", "protojsonschema_rule")

licenses(["notice"]) # Apache 2

protojsonschema_rule(
name = "api_protojsonschema",
visibility = ["//visibility:public"],
deps = [
"@envoy_api//envoy/config/bootstrap/v2:pkg",
"@envoy_api//envoy/config/bootstrap/v3:pkg",
],
)
39 changes: 39 additions & 0 deletions tools/protojsonschema_with_aspects/protojsonschema.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
load("@rules_proto//proto:defs.bzl", "ProtoInfo")
load("//tools/api_proto_plugin:plugin.bzl", "api_proto_plugin_aspect", "api_proto_plugin_impl")

def _protojsonschema_impl(target, ctx):
return api_proto_plugin_impl(
target = target,
ctx = ctx,
output_group = "proto",
mnemonic = "protojsonschema",
output_suffixes = [],
output_dir = "jsonschema",
)

protojsonschema_aspect = api_proto_plugin_aspect(
"@com_github_chrusty_protoc_gen_jsonschema//cmd/protoc-gen-jsonschema",
_protojsonschema_impl,
)

def _protojsonschema_rule_impl(ctx):
return [
DefaultInfo(
files = depset(
transitive = [
depset([
path
for dep in ctx.attr.deps
for path in dep[OutputGroupInfo].proto.to_list()
]),
],
),
),
]

protojsonschema_rule = rule(
implementation = _protojsonschema_rule_impl,
attrs = {
"deps": attr.label_list(aspects = [protojsonschema_aspect]),
},
)

0 comments on commit e6e5e9a

Please sign in to comment.