diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index dc1f993..8514878 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,5 +1,5 @@ { - "lockFileVersion": 26, + "lockFileVersion": 28, "registryFileHashes": { "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", @@ -202,7 +202,7 @@ "moduleExtensions": { "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "Ga4z8lQy1YQ5rAMy+dOl0dqcCEBnYNCXku8x3YQmDZI=", + "bzlTransitiveDigest": "+Kp6j204mBZ3mxlIDDR0gBoP45BZ4jYRhRAcB8sU0qc=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedInputs": [ "REPO_MAPPING:rules_kotlin+,bazel_tools bazel_tools" @@ -259,7 +259,7 @@ }, "@@rules_python+//python/extensions:config.bzl%config": { "general": { - "bzlTransitiveDigest": "iibnRYgg8LpcfmH7EAnVwYePC3jsVaJ6Id8XxUjSZps=", + "bzlTransitiveDigest": "dzD8Q2YmrP3fz8saWLHPmlwPLO91ImtTmP/c9JKTStM=", "usagesDigest": "ZVSXMAGpD+xzVNPuvF1IoLBkty7TROO0+akMapt1pAg=", "recordedInputs": [ "REPO_MAPPING:rules_python+,bazel_tools bazel_tools", @@ -456,5 +456,6 @@ } } }, - "facts": {} + "facts": {}, + "factsVersions": {} } diff --git a/README.md b/README.md index cb42e61..44f8c15 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,22 @@ This also works for directories. To validate all `diff`, `cmp`, etc. actions by default, set `common --@diff.bzl//diff:validate=true` in your bazelrc. -### Support automatic patch workflows for CI +### Support automatic patch workflows -This ruleset outputs patches into a distinct `diff_bzl__patch` output group making it easier for patches to be collected and then applied using automation. See the [build & patch](./examples/build-and-patch.sh) example script. +This ruleset outputs patches for source tree files into the `diff_bzl__patch` output group by default, making it easier to collect and apply patches in an automated fashion. See the [build & patch](./examples/build-and-patch.sh) example script. + +The output groups for source patches can be customized via the `source_patch_output_groups` attribute. This can be used to categorize types of patches. For example, `diff` targets with `source_patch_output_groups = ["autopatch"]` might identify patches that should automatically be applied if the build fails on CI, whereas a `"snapshot_test"` group could identify patches that require human review, etc. + +```starlark +diff( + name = "foo", + srcs = [ + "foo.pb.go", + ":foo_generated", + ], + validate = 1, + source_patch_output_groups = ["autopatch"], +) +``` + +NB: Only patches for files in the source tree are placed in the above output group(s). Patches for files in the output tree are not included as they should not be modified outside of Bazel. diff --git a/diff/defs.bzl b/diff/defs.bzl index a140945..4cea988 100644 --- a/diff/defs.bzl +++ b/diff/defs.bzl @@ -55,7 +55,7 @@ def cmp(name, srcs, args = [], out = None, **kwargs): **kwargs ) -def diff(name, srcs, args = ["--unified"], patch = None, **kwargs): +def diff(name, srcs, args = ["--unified"], patch = None, source_patch_output_groups = ["diff_bzl__patch"], **kwargs): """Runs a diff between files and return a patch. Examples: @@ -98,9 +98,10 @@ def diff(name, srcs, args = ["--unified"], patch = None, **kwargs): Args: name: The name of the rule - srcs: The files to compare. + srcs: The files to compare args: Additional arguments to pass to diff patch: The output file to write the diff to. Defaults to ${name}.patch. + source_patch_output_groups: Additional output groups to add source file patches to **kwargs: Additional arguments to pass to the underlying rule """ for i in range(len(srcs)): @@ -114,6 +115,7 @@ def diff(name, srcs, args = ["--unified"], patch = None, **kwargs): args = args, srcs = srcs, patch = patch or name + ".patch", + source_patch_output_groups = source_patch_output_groups, **kwargs ) diff --git a/diff/private/diff.bzl b/diff/private/diff.bzl index 58916f4..ecc1c99 100644 --- a/diff/private/diff.bzl +++ b/diff/private/diff.bzl @@ -229,18 +229,28 @@ def _diff_rule_impl(ctx): ERROR: diff command exited with non-zero status. {}""".format(patch_msg))) + source_patches_depset = depset(source_patch_outputs) + + source_patch_output_groups = {group: source_patches_depset for group in ctx.attr.source_patch_output_groups} + return [ DefaultInfo(files = depset(outputs)), OutputGroupInfo( _validation = depset(validation_outputs), - # By reading the Build Events, a Bazel wrapper can identify this diff output group and apply the patch. - diff_bzl__patch = depset(source_patch_outputs), + **source_patch_output_groups ), ] diff_rule = rule( implementation = _diff_rule_impl, attrs = { + "source_patch_output_groups": attr.string_list( + doc = """\ + Additional output groups to add source file patches to. Output groups can be used to categorize sets of patches + that a Bazel wrapper can identify and apply by reading build events. + """, + default = ["diff_bzl__patch"], + ), "args": attr.string_list( doc = """\ Additional arguments to pass to the diff command. diff --git a/diff/tests/BUILD.bazel b/diff/tests/BUILD.bazel index 6fda3b9..7521b6d 100644 --- a/diff/tests/BUILD.bazel +++ b/diff/tests/BUILD.bazel @@ -265,6 +265,53 @@ diff( validate = 1, ) +diff( + name = "case_source_patch_default_output_group", + srcs = [ + "case_source_patch_default_output_group.a", + "case_source_patch_default_output_group.b", + ], +) + +filegroup( + name = "case_source_patch_default_output_group_output", + srcs = [":case_source_patch_default_output_group"], + output_group = "diff_bzl__patch", +) + +diff( + name = "case_source_patch_default_output_group_assert_patch", + srcs = [ + "case_source_patch_default_output_group.golden.patch", + ":case_source_patch_default_output_group_output", + ], + validate = 1, +) + +diff( + name = "case_source_patch_custom_output_group", + srcs = [ + "case_source_patch_custom_output_group.a", + "case_source_patch_custom_output_group.b", + ], + source_patch_output_groups = ["autopatch"], +) + +filegroup( + name = "case_source_patch_custom_output_group_output", + srcs = [":case_source_patch_custom_output_group"], + output_group = "autopatch", +) + +diff( + name = "case_source_patch_custom_output_group_assert_patch", + srcs = [ + "case_source_patch_custom_output_group.golden.patch", + ":case_source_patch_custom_output_group_output", + ], + validate = 1, +) + genrule( name = "case_diff_genrule", srcs = [], @@ -536,6 +583,12 @@ build_test( ":case_to_file_directory_assert_patch", ":case_to_file_directory_newfile", ":case_to_file_directory_newfile_assert_patch", + ":case_source_patch_default_output_group", + ":case_source_patch_default_output_group_output", + ":case_source_patch_default_output_group_assert_patch", + ":case_source_patch_custom_output_group", + ":case_source_patch_custom_output_group_output", + ":case_source_patch_custom_output_group_assert_patch", ":case_diff_genrule", ":case_cmp_genrule", ":case_cmp_same_binary", diff --git a/diff/tests/case_source_patch_custom_output_group.a b/diff/tests/case_source_patch_custom_output_group.a new file mode 100644 index 0000000..eec8c88 --- /dev/null +++ b/diff/tests/case_source_patch_custom_output_group.a @@ -0,0 +1 @@ +moo diff --git a/diff/tests/case_source_patch_custom_output_group.b b/diff/tests/case_source_patch_custom_output_group.b new file mode 100644 index 0000000..cab3a22 --- /dev/null +++ b/diff/tests/case_source_patch_custom_output_group.b @@ -0,0 +1 @@ +cow diff --git a/diff/tests/case_source_patch_custom_output_group.golden.patch b/diff/tests/case_source_patch_custom_output_group.golden.patch new file mode 100644 index 0000000..d7252ad --- /dev/null +++ b/diff/tests/case_source_patch_custom_output_group.golden.patch @@ -0,0 +1,5 @@ +--- diff/tests/case_source_patch_custom_output_group.a 1970-01-01 00:00:00.000000000 +0000 ++++ diff/tests/case_source_patch_custom_output_group.b 1970-01-01 00:00:00.000000000 +0000 +@@ -1 +1 @@ +-moo ++cow diff --git a/diff/tests/case_source_patch_default_output_group.a b/diff/tests/case_source_patch_default_output_group.a new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/diff/tests/case_source_patch_default_output_group.a @@ -0,0 +1 @@ +foo diff --git a/diff/tests/case_source_patch_default_output_group.b b/diff/tests/case_source_patch_default_output_group.b new file mode 100644 index 0000000..5716ca5 --- /dev/null +++ b/diff/tests/case_source_patch_default_output_group.b @@ -0,0 +1 @@ +bar diff --git a/diff/tests/case_source_patch_default_output_group.golden.patch b/diff/tests/case_source_patch_default_output_group.golden.patch new file mode 100644 index 0000000..866680e --- /dev/null +++ b/diff/tests/case_source_patch_default_output_group.golden.patch @@ -0,0 +1,5 @@ +--- diff/tests/case_source_patch_default_output_group.a 1970-01-01 00:00:00.000000000 +0000 ++++ diff/tests/case_source_patch_default_output_group.b 1970-01-01 00:00:00.000000000 +0000 +@@ -1 +1 @@ +-foo ++bar diff --git a/docs/rules.md b/docs/rules.md index 39d2cc4..ea64f61 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -59,7 +59,7 @@ genrule(
load("@diff.bzl//diff:defs.bzl", "diff")
-diff(name, srcs, args, patch, **kwargs)
+diff(name, srcs, args, patch, source_patch_output_groups, **kwargs)
Runs a diff between files and return a patch.
@@ -109,9 +109,10 @@ to `args`. If overriding arguments, --unified must be added explicitly._
| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| name | The name of the rule | none |
-| srcs | The files to compare. | none |
+| srcs | The files to compare | none |
| args | Additional arguments to pass to diff | `["--unified"]` |
| patch | The output file to write the diff to. Defaults to ${name}.patch. | `None` |
+| source_patch_output_groups | Additional output groups to add source file patches to | `["diff_bzl__patch"]` |
| kwargs | Additional arguments to pass to the underlying rule | none |