diff --git a/docs/design/prototiller/prototiller-reqs-for-editions.md b/docs/design/prototiller/prototiller-reqs-for-editions.md new file mode 100644 index 0000000000..48503296a1 --- /dev/null +++ b/docs/design/prototiller/prototiller-reqs-for-editions.md @@ -0,0 +1,138 @@ +# Prototiller Requirements for Editions + +**Author:** [@mcy](https://github.com/mcy) + +**Approved:** 2022-11-29 + +## Background + +Prototiller is Protobuf's new mass refactoring Swiss army knife, similar to +Buildozer. We plan to use Prototiller to enable LSCs within google3 and to allow +users (internal and external) to modify `.proto` files safely. + +Prototiller is being developed as part of the +[Editions](../editions/what-are-protobuf-editions.md) project, and will +prioritize enabling Editions-related refactorings to unblock Editions migrations +in 2023. This document describes the relevant requirements. + +## Overview + +*Protochangifier Semantic Actions* (not available externally) describes the +original design for the Prototiller interface; it would consume a Protobuf +message that described changes to apply to a `.proto` file passed as input. In +this document, we prescribe a variant of this interface that fulfills *only* the +needs of Editions, while remaining extensible for future change actions. + +Broad requirements are as follows: + +* Actions must include the following Editions-oriented upgrade workflows: + * Upgrade a file to a particular edition, regardless of whether it's in + syntax mode or editions mode, updating features in such a way to be a + no-op. **This is the highest-priority workflow.** + * "Clean up" features in a particular file: i.e., run a simple algorithm + to determine the smallest set of features that need to be present at + each level of the file. + * Modify features from a particular syntax element. +* Actions must be both specific to particular syntax elements (for when change + specs are checked in alongside `.proto` files by Schema Consumers), and + generic (so that a single change spec or set of change specs can power a + large-scale change). + +In this document we provide a recommendation for a Protobuf schema based on the +original Protochangifier design, but geared towards these specific needs. + +This is only a recommendation; the Prototiller project owners should modify this +to suit the implementation; only the requirements in this document are binding, +and the schema is merely an illustration of those requirements. + +The suggested schema is as follows. + +``` +syntax = "proto2"; + +package prototiller; + +// This is the proto that Prototiller accepts as input. +message ChangeSpec { + // Actions to execute on the file. + repeated Action actions = 1; + + // Some changes may result in a wireformat break; changing field type is + // usually unsafe. By default, Prototiller does not allow such changes, + // users can set allow_unsafe_wire_format_changes to true to force the change. + optional bool allow_unsafe_wire_format_changes = 2 [default = false]; + optional bool allow_unsafe_text_format_changes = 3 [default = false]; + optional bool allow_unsafe_json_format_changes = 4 [default = false]; +} + +// A single action. See messages below for description of their +// semantics. +message Action { + oneof kind { + UpgradeEdition upgrade_edition = 20; + CleanUpFeatures clean_up_features = 21; + ModifyFeature modify_feature = 22; + } +} + +// Upgrades the edition of a file to a specified edition. +// Treats syntax mode as being a weird, special edition that cannot be +// upgraded to. +// +// This action is always safe. +message UpgradeEdition { + // The edition to upgrade to. + optional string edition = 1; +} + +// Cleans up features in a file, such that there are as few explicitly set +// features as necessary. +// +// This action is always safe. +message CleanUpFeatures {} + +// Modifies a specific feature on all syntax elements that match and which can +// host that particular feature. +// +// Prototiller must be aware of which changes affect wire format, so that it +// can flag them as unsafe. +message ModifyFeature { + // The name of the feature to modify. + repeated proto2.UninterpretedOption.NamePart feature = 1; + + // A pattern for matching paths to syntax elements to modify. + // + // Elements of this field can either be identifiers, or the string "*", which + // matches all identifiers. Thus, ["foo", "Bar"] matches the message foo.Bar, + // ["foo", "Bar", "*"] matches all fields and nested types of foo.Bar + // (recursively), and ["*"] matches all elements of a file. + repeated string path_pattern = 2; + + // The value to set the feature to. If not set, this means that the + // feature should be deleted. + oneof value { + int64 int_value = 20; + double double_value = 21; + // ... and so on. + } +} +``` + +## Alternatives Considered + +This document does not capture a design so much as requirements that a design +must satisfy, so we will be brief on potential alternatives to the requirements, +and why we decided against them. + +* Omit feature cleanup as its own action, and let it happen implicitly as part + of other actions. + * It is desirable to be able to aggressively run this operation + everywhere, potentially even as part of "format on save" in Cider and + other IDEs. +* Make `ModifyFeature` operate on all syntax elements of a file + simultaneously. + * `ModifyFeature` is intended so that SchemaConsumers can affect + fine-grained control of features in `.proto` files they import. Users + will want to be able to wipe out a feature from all fields in a file, or + perhaps just on a handful of fields they care about. Offering simple + pattern-matching supports both.