New to Kestra?
Use blueprints to kickstart your first workflows.
Block Protobuf schema changes that break wire compatibility while only warning on reflection/JSON-only changes.
A schema gate that fails on every finding buf reports trains teams to bypass it, because plenty of findings (a changed JSON field name, a changed default value) affect reflection or JSON serialization but not the binary wire format existing compiled clients and stored messages depend on. This blueprint runs buf's own breaking-change detector between a schema's previous and current version, splits findings into "breaks wire compatibility or removes something still referenced" and "affects reflection or JSON only," and only fails the execution outright on the first group, while treating a schema that fails to even compile as its own distinct failure mode rather than silently reporting zero breaking changes.
check_on_pr_webhook (io.kestra.plugin.core.trigger.Webhook) lets a CI pipeline POST a pull request's new .proto file to this flow directly. fetchType: STORE streams that POST body into Kestra's internal storage and exposes it as trigger.uri, a safe file reference, rather than as raw trigger.body text, which the trigger's own documentation flags as untrusted input unsafe to interpolate into a script task's commands. Running this flow manually or as a Subflow instead uses the current_proto_file input, the same kind of safe file reference; previous_proto_file is always supplied as an input either way.check_breaking_changes (io.kestra.plugin.scripts.shell.Commands) runs bufbuild/buf's own container. buf CLI v2 requires a minimal buf.yaml to treat a bare directory as a module, so one is generated for both the workspace and a baseline/ directory holding the previous schema, then buf breaking --against baseline --error-format=json runs. Its JSON output is newline-delimited (one object per line), not a single array, so awk converts it to a proper array before Kestra's jq filter (which expects one JSON document) ever reads it. stdout (report.json, the violations) and stderr (stderr.txt, a schema compile error) are captured separately, because buf breaking returns the same exit code for both a validation failure and a tool/compile error, so the exit code alone cannot tell them apart.classify_schema_compatibility (io.kestra.plugin.core.debug.Return) checks whether both output files exist; if not, UNREACHABLE. Otherwise a non-empty stderr.txt (the schema did not even compile) or any finding whose type is a *_NO_DELETE rule, FIELD_SAME_TYPE, RPC_SAME_REQUEST_TYPE, or RPC_SAME_RESPONSE_TYPE (wire-level breaking changes) classifies BLOCK; any remaining finding classifies WARN; otherwise PASS.route_by_schema_compatibility (io.kestra.plugin.core.flow.Switch) branches on that string: PASS logs a quiet confirmation, WARN pages Slack with the non-breaking finding types but lets the execution succeed, BLOCK pages Slack with either the compile error or the specific breaking rule types and then runs fail_gate (io.kestra.plugin.core.execution.Fail) to deliberately fail the execution, and UNREACHABLE pages Slack separately.log_audit_status records a status line whenever the execution reaches it, i.e. every outcome except BLOCK, which fails before reaching this task by design.errors block fires both for a genuine flow break and, intentionally, every time fail_gate fires, a chained parent pipeline sees either case as the same FAILED execution and stops.fail_gate makes the execution itself FAILED on a breaking change, so a parent flow using this as a Subflow task before a schema registry push or a service deploy is actually blocked, not just notified into a channel someone might not read before merging.status output (PASS / WARN / BLOCK / UNREACHABLE) you can also read directly if you prefer branching in the caller instead of relying on failure propagation.buf produces the findings; nothing in the CLI itself enforces a policy or blocks a deploy pipeline on your terms, and its exit code conflates a real validation failure with a tool error. Kestra turns that output into an actual pass/fail decision that correctly separates those two failure modes, with a real execution-history trail of exactly which rule types triggered a block versus a warning, and Fail gives a parent pipeline a real signal to stop on instead of a Slack message it has to notice in time.
bufbuild/buf image, no schema registry, server, or account is required; buf compares the two schema files directly..proto file and the new pending .proto file.SCHEMA_CHECK_WEBHOOK_KEY: the key segment of the webhook URL (.../executions/webhook/{namespace}/{flowId}/{key}). Only needed if you enable check_on_pr_webhook; a manual or Subflow run using current_proto_file does not need it.SLACK_WEBHOOK_URL: Slack incoming webhook used for the WARN alert, the BLOCK alert, the unreachable alert, and the flow-failure alert.SECRET_SCHEMA_CHECK_WEBHOOK_KEY and SECRET_SLACK_WEBHOOK_URL as base64, for example echo -n "<your-value>" | base64.SLACK_WEBHOOK_URL secret, and the SCHEMA_CHECK_WEBHOOK_KEY secret if you plan to enable the webhook..proto files: a baseline, and a modified copy that both removes an RPC method (a wire-breaking change) and renames a field's JSON name (a non-breaking change), to exercise both BLOCK and WARN in one pair (or split them into two runs).previous_proto_file and current_proto_file, run the flow manually, and confirm status matches what you expect.check_on_pr_webhook and have your CI pipeline curl --data-binary @new_schema.proto the webhook URL on each pull request, or wire a Subflow task pointing at this flow into your pipeline immediately before a schema registry push or service deploy.--against at a .git#branch=main reference instead of a second file input, if the Kestra worker has access to the schema's git history, to avoid needing a separately supplied baseline file.buf lint step alongside buf breaking, so style and naming convention violations surface in the same gate without being treated as breaking changes.