Files
cdesouza-chromium a651e03a79 [plaster] Introducing a 🩹 YAML frontend (#36651)
This PR introduces the YAML frontend for plaster that is meant to
eventually become the only frontend, once all `.toml` Plaster files are
migrated.

This change covers all places where the assumption about `.toml` files
was being used, inclusing `brockit`, and `git-cr` tools.

Most of the code that should be deleted in the future is well guarded
with comments leading back to the issue tracking, so the TOML parser can
be dropped eventually.

With this change, a dependency to `pyyaml` has been introduced. This
dependency has wheels provided by `vpython`, which is already the
expected python runtime for Plaster.

 - Rationale for this change

We have experimented at length with `.toml` files, in order to
understand some of the shortcomings they have that would be addressed
with `.yaml` files.

 * `prettier` offers YAML formatting out-of-the-box. On the other hand,
   formatters for TOML files are not easy to find, as both `prettier`
   and `vpython` have their own challanges with the current options
 * YAML's sytanx works better with codeblocks, as it doesn't require
   quoting. This makes the content seen less noisy. Looking on some of
   the migrated plasters, the YAML substitutions look more readable.
 * YAML has better sytanx highlight support in some editors.

Bug: https://github.com/brave/brave-browser/issues/55738
2026-05-22 15:20:51 +01:00

192 lines
6.6 KiB
Markdown

# 🩹 _Plaster_ and semantical patching
_Plaster_ is an experimental tool being introduced in Brave to allow us to apply
changes to upstream sources files, by relying on regex transformations to search
for patterns and apply substitutions.
## Why 🩹 _Plaster_
The two traditional approaches to introduce changes to Chromium have been `git`
patches (i.e. `patches/`), and language overrides (i.e. `chromium_src/`). The
use of patches is in general avoided whenever possible, as they tend to easily
run into conflicts when rebasing Brave onto a newer Chromium version. With
language overrides, although more flexible than patches, they tend to be
invisible in the source code target, hard to interpret, and lead to many cases
of unintended replacements.
Using regexes, _Plaster_ avoids the brittleness of patch files, while providing
matching mechanisms that are more flexible than the methods currently employed
for macro replacement. This also means a language-agnostic way to approach
source changes semantically, and in place. _Plaster_ changes can be both seen in
place, as well as audited as patch files.
## How does it work
_Plaster_ files are placed under `rewrite/`, using a `.yaml` extension, and they
are supposed to match the path for the file being plastered. A deprecated
`.toml` form is also accepted while existing plasters are being migrated — see
[Legacy TOML format (deprecated)](#legacy-toml-format-deprecated) at the end of
this document.
> [!WARNING]
>
> At the moment, only plaster files to sources in Chromium's `src` repo are
> supported.
Each plaster file will be used to apply changes into a given source, and then
generate a patch for the effected changes.
For example, imagine you want to append Brave-specific entries to the upstream
`RequestType` enum in `components/permissions/request_type.h`. You would care
about the following files.
- **Plaster file:** `brave/rewrite/components/permissions/request_type.h.yaml`
- **Source file:** `components/permissions/request_type.h`
- **Patch file:** `brave/patches/components-permissions-request_type.h.patch`
### Creating a plaster file
A plaster file is a YAML file that lists regex operations to be applied to the
corresponding source, based on its path. The following plaster appends
Brave-specific values to the upstream `RequestType` enum.
Creating the source file with `vscode`:
```sh
code rewrite/components/permissions/request_type.h.yaml
```
We can use a regex that anchors on the enum's outer braces and on the existing
`kMaxValue = <something>` line, and then inserts the Brave entries plus a new
`kMaxValue` just before the closing `}`:
```yaml
# Copyright (c) 2026 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at https://mozilla.org/MPL/2.0/.
substitutions:
- description: |
Append Brave-specific entries to `RequestType`
This plaster is generic enough to guarantee that our entries are always last,
and that they also become the new kMaxValue.
re_pattern: '(enum class RequestType \{.+?,)(\s+)kMaxValue = \w+'
re_flags: [DOTALL]
replace: |-
\1
kWidevine,
kBraveEthereum,
kBraveSolana,
kBraveOpenAIChat,
kBraveGoogleSignInPermission,
kBraveCardano,
kBraveMinValue = kWidevine,
kBraveMaxValue = kBraveCardano,
kMaxValue = kBraveCardano
```
_Plaster_ substitutions are listed under `substitutions:`. _Plaster_ loads the
contents of a source from `git`, as the source of truth, not from whatever is on
disk, and then it applies each substitution cumulatively to the contents of the
target source, updating the upstream source at the end.
Each entry has the following format:
```yaml
substitutions:
- description: ''
# One of either pattern or re_pattern must be specified.
pattern: '' # non-regex pattern (string will be escaped)
re_pattern: '' # regex pattern
replace: ''
re_flags: [] # traditional Python `re` flag names, e.g. [DOTALL]
count: 1 # 1 is the default; 0 means no count, not advised.
```
Use YAML's `|` / `|-` block scalars when you need multi-line `replace` or
`description` values — `|` keeps a trailing newline, `|-` strips it.
Single-quoted YAML strings preserve backslashes literally, which is useful for
regex patterns (`'\s'` stays as the two characters `\s`, not interpreted by
YAML).
To apply this plaster file, just run `plaster.py`:
```sh
tools/cr/plaster.py apply
```
Running `plaster.py` will cause the substitution to be applied in
`components/permissions/request_type.h`, and trigger the creation or update of
the corresponding patch file for this source. For example, it may produce a diff
file like this:
```patch
diff --git a/patches/components-permissions-request_type.h.patch b/patches/components-permissions-request_type.h.patch
new file mode 100644
index 00000000000..bec88027991
--- /dev/null
+++ b/patches/components-permissions-request_type.h.patch
@@ -0,0 +1,20 @@
+diff --git a/components/permissions/request_type.h b/components/permissions/request_type.h
+--- a/components/permissions/request_type.h
++++ b/components/permissions/request_type.h
+@@ -... @@ enum class RequestType {
+ ...,
+ kStorageAccess,
+ kWindowManagement,
+- kMaxValue = kWindowManagement,
++ kWidevine,
++ kBraveEthereum,
++ kBraveSolana,
++ kBraveOpenAIChat,
++ kBraveGoogleSignInPermission,
++ kBraveCardano,
++ kBraveMinValue = kWidevine,
++ kBraveMaxValue = kBraveCardano,
++ kMaxValue = kBraveCardano,
+ };
```
So at the end, you get a regular patch, and everything works as usual with how
our Brave machinery handles patch files.
### Checking if files are up-to-date
You can verify if all plaster files are applied, with Chromium sources updated
and patch files checked under `path` with the following command:
```sh
tools/cr/plaster.py check
```
This is the equivalent of a dry run of `plaster.py apply`.
### Best practices
See
https://github.com/brave-experiments/brave-core-tools/blob/master/docs/best-practices/plaster.md
### Legacy TOML format (deprecated)
> [!IMPORTANT]
>
> The `.toml` form is **deprecated** and kept only so existing plasters keep
> working until they are migrated. New plasters must use `.yaml` while the
> existing `.toml` ones are migrated. Migration progress is tracked in
> [brave/brave-browser#55738](https://github.com/brave/brave-browser/issues/55738).
Pre-existing plasters under `rewrite/` use a `.toml` file with the same fields
as their YAML counterparts, written as TOML array-of-tables:
```toml
[[substitution]]
description = ''
re_pattern = ''
pattern = ''
replace = ''
re_flags = []
count = 1
```