From bd79f922c9e307f7cd2a5179c957189e7bf185fb Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Tue, 29 Mar 2022 12:14:07 -0700 Subject: [PATCH] Add diff_test asserting that docs are up-to-date (#321) Based on Aspect's stardoc_with_diff_test; see https://github.com/aspect-build/bazel-lib/blob/main/docs/docs.md --- .bazelci/presubmit.yml | 2 + docs/BUILD | 161 ++++++++++-------------- docs/private/BUILD | 1 + docs/private/stardoc_with_diff_test.bzl | 96 ++++++++++++++ docs/regenerate_docs.sh | 27 ---- 5 files changed, 163 insertions(+), 124 deletions(-) create mode 100644 docs/private/BUILD create mode 100644 docs/private/stardoc_with_diff_test.bzl delete mode 100755 docs/regenerate_docs.sh diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index d7e4845..fc40687 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -46,6 +46,7 @@ tasks: # TODO(laszlocsomor): remove "--test_env=LOCALAPPDATA" after # https://github.com/bazelbuild/bazel/issues/7761 is fixed - "--test_env=LOCALAPPDATA" + - "--test_tag_filters=-no_windows" ubuntu1804_last_green: name: "Last Green Bazel" @@ -93,5 +94,6 @@ tasks: # TODO(laszlocsomor): remove "--test_env=LOCALAPPDATA" after # https://github.com/bazelbuild/bazel/issues/7761 is fixed - "--test_env=LOCALAPPDATA" + - "--test_tag_filters=-no_windows" buildifier: latest diff --git a/docs/BUILD b/docs/BUILD index 2ccd60e..873e26c 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -1,137 +1,104 @@ -load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc") +load("//docs/private:stardoc_with_diff_test.bzl", "stardoc_with_diff_test", "update_docs") licenses(["notice"]) -# Note that due to a bug in Bazel 0.22.0, these targets cannot be built without -# the --incompatible_remap_main_repo flag. -stardoc( - name = "build_test_docs", - out = "build_test_doc_gen.md", - input = "//rules:build_test.bzl", - deps = ["//rules:build_test"], +stardoc_with_diff_test( + bzl_library_target = "//rules:analysis_test", + out_label = "//docs:analysis_test_doc.md", ) -stardoc( - name = "analysis_test_docs", - out = "analysis_test_doc_gen.md", - input = "//rules:analysis_test.bzl", +stardoc_with_diff_test( + bzl_library_target = "//rules:build_test", + out_label = "//docs:build_test_doc.md", ) -stardoc( - name = "collections_docs", - out = "collections_doc_gen.md", - input = "//lib:collections.bzl", - deps = ["//lib:collections"], +stardoc_with_diff_test( + bzl_library_target = "//lib:collections", + out_label = "//docs:collections_doc.md", ) -stardoc( - name = "dicts_docs", - out = "dicts_doc_gen.md", - input = "//lib:dicts.bzl", - deps = ["//lib:dicts"], +stardoc_with_diff_test( + bzl_library_target = "//rules:common_settings", + out_label = "//docs:common_settings_doc.md", ) -stardoc( - name = "partial_docs", - out = "partial_doc_gen.md", - input = "//lib:partial.bzl", - deps = ["//lib:partial"], +stardoc_with_diff_test( + bzl_library_target = "//rules:copy_file", + out_label = "//docs:copy_file_doc.md", ) -stardoc( - name = "paths_docs", - out = "paths_doc_gen.md", - input = "//lib:paths.bzl", - deps = ["//lib:paths"], +stardoc_with_diff_test( + bzl_library_target = "//lib:dicts", + out_label = "//docs:dicts_doc.md", ) -stardoc( - name = "selects_docs", - out = "selects_doc_gen.md", - input = "//lib:selects.bzl", - deps = ["//lib:selects"], +stardoc_with_diff_test( + bzl_library_target = "//rules:diff_test", + out_label = "//docs:diff_test_doc.md", ) -stardoc( - name = "new_sets_docs", - out = "new_sets_doc_gen.md", - input = "//lib:new_sets.bzl", - deps = ["//lib:new_sets"], +stardoc_with_diff_test( + bzl_library_target = "//rules:native_binary", + out_label = "//docs:native_binary_doc.md", ) -stardoc( - name = "shell_docs", - out = "shell_doc_gen.md", - input = "//lib:shell.bzl", - deps = ["//lib:shell"], +stardoc_with_diff_test( + bzl_library_target = "//lib:new_sets", + out_label = "//docs:new_sets_doc.md", ) -stardoc( - name = "structs_docs", - out = "structs_doc_gen.md", - input = "//lib:structs.bzl", - deps = ["//lib:structs"], +stardoc_with_diff_test( + bzl_library_target = "//lib:partial", + out_label = "//docs:partial_doc.md", ) -stardoc( - name = "types_docs", - out = "types_doc_gen.md", - input = "//lib:types.bzl", - deps = ["//lib:types"], +stardoc_with_diff_test( + bzl_library_target = "//lib:paths", + out_label = "//docs:paths_doc.md", ) -stardoc( - name = "unittest_docs", - out = "unittest_doc_gen.md", - input = "//lib:unittest.bzl", - deps = ["//lib:unittest"], +stardoc_with_diff_test( + bzl_library_target = "//rules:run_binary", + out_label = "//docs:run_binary_doc.md", ) -stardoc( - name = "versions_docs", - out = "versions_doc_gen.md", - input = "//lib:versions.bzl", - deps = ["//lib:versions"], +stardoc_with_diff_test( + bzl_library_target = "//lib:selects", + out_label = "//docs:selects_doc.md", ) -stardoc( - name = "copy_file_docs", - out = "copy_file_doc_gen.md", - input = "//rules:copy_file.bzl", - deps = ["//rules:copy_file"], +stardoc_with_diff_test( + bzl_library_target = "//lib:shell", + out_label = "//docs:shell_doc.md", ) -stardoc( - name = "write_file_docs", - out = "write_file_doc_gen.md", - input = "//rules:write_file.bzl", - deps = ["//rules:write_file"], +stardoc_with_diff_test( + bzl_library_target = "//lib:structs", + out_label = "//docs:structs_doc.md", ) -stardoc( - name = "diff_test_docs", - out = "diff_test_doc_gen.md", - input = "//rules:diff_test.bzl", - deps = ["//rules:diff_test"], +stardoc_with_diff_test( + bzl_library_target = "//lib:types", + out_label = "//docs:types_doc.md", ) -stardoc( - name = "native_binary_docs", - out = "native_binary_doc_gen.md", - input = "//rules:native_binary.bzl", - deps = ["//rules:native_binary"], +stardoc_with_diff_test( + bzl_library_target = "//lib:unittest", + out_label = "//docs:unittest_doc.md", ) -stardoc( - name = "run_binary_docs", - out = "run_binary_doc_gen.md", - input = "//rules:run_binary.bzl", - deps = ["//rules:run_binary"], +stardoc_with_diff_test( + bzl_library_target = "//lib:versions", + out_label = "//docs:versions_doc.md", ) -stardoc( - name = "common_settings_docs", - out = "common_settings_doc_gen.md", - input = "//rules:common_settings.bzl", - deps = ["//rules:common_settings"], +stardoc_with_diff_test( + bzl_library_target = "//rules:write_file", + out_label = "//docs:write_file_doc.md", +) + +# update_docs must be at the bottom of the BUILD file +update_docs( + name = "update", + docs_folder = "docs", ) diff --git a/docs/private/BUILD b/docs/private/BUILD new file mode 100644 index 0000000..f8c832b --- /dev/null +++ b/docs/private/BUILD @@ -0,0 +1 @@ +# No targets in this package diff --git a/docs/private/stardoc_with_diff_test.bzl b/docs/private/stardoc_with_diff_test.bzl new file mode 100644 index 0000000..dc23deb --- /dev/null +++ b/docs/private/stardoc_with_diff_test.bzl @@ -0,0 +1,96 @@ +"""Helpers for keeping stardoc documentation up-to-date. + +These are currently a private API in bazel-skylib. +See discussion: +https://github.com/bazelbuild/bazel-skylib/pull/321#issuecomment-1081166216 + +If you need a similar feature, you can consider using a similar rule +available from a third-party: +https://github.com/aspect-build/bazel-lib/blob/main/docs/docs.md +""" + +load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc") +load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") + +def stardoc_with_diff_test( + bzl_library_target, + out_label): + """Creates a stardoc target coupled with a `diff_test` for a given `bzl_library`. + + This is helpful for minimizing boilerplate in repos wih lots of stardoc targets. + + Args: + bzl_library_target: the label of the `bzl_library` target to generate documentation for + out_label: the label of the output MD file + """ + + out_file = out_label.replace("//", "").replace(":", "/") + + # Generate MD from .bzl + stardoc( + name = out_file.replace("/", "_").replace(".md", "-docgen"), + out = out_file.replace(".md", "-docgen.md"), + input = bzl_library_target + ".bzl", + deps = [bzl_library_target], + ) + + # Ensure that the generated MD has been updated in the local source tree + diff_test( + name = out_file.replace("/", "_").replace(".md", "-difftest"), + failure_message = "Please run \"bazel run //docs:update\"", + # Source file + file1 = out_label, + # Output from stardoc rule above + file2 = out_file.replace(".md", "-docgen.md"), + tags = ["no_windows"], + ) + +def update_docs( + name = "update", + docs_folder = "docs"): + """Creates a `sh_binary` target which copies over generated doc files to the local source tree. + + This is to be used in tandem with `stardoc_with_diff_test()` to produce a convenient workflow + for generating, testing, and updating all doc files as follows: + + ``` bash + bazel build //{docs_folder}/... && bazel test //{docs_folder}/... && bazel run //{docs_folder}:update + ``` + + eg. + + ``` bash + bazel build //docs/... && bazel test //docs/... && bazel run //docs:update + ``` + + The `update_docs()` invocation must come after/below any `stardoc_with_diff_test()` invocations + in the BUILD file. + + Args: + name: the name of the `sh_binary` target + docs_folder: the name of the folder containing the doc files in the local source tree + """ + content = ["#!/usr/bin/env bash", "cd ${BUILD_WORKSPACE_DIRECTORY}"] + data = [] + for r in native.existing_rules().values(): + if r["kind"] == "stardoc": + doc_gen = r["out"] + if doc_gen.startswith(":"): + doc_gen = doc_gen[1:] + doc_dest = doc_gen.replace("-docgen.md", ".md") + data.append(doc_gen) + content.append("cp -fv bazel-bin/{0}/{1} {2}".format(docs_folder, doc_gen, doc_dest)) + + update_script = name + ".sh" + write_file( + name = "gen_" + name, + out = update_script, + content = content, + ) + + native.sh_binary( + name = name, + srcs = [update_script], + data = data, + ) diff --git a/docs/regenerate_docs.sh b/docs/regenerate_docs.sh deleted file mode 100755 index d16ea63..0000000 --- a/docs/regenerate_docs.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# A script to manually regenerate the documentation contained in the docs/ directory. -# Should be run from the WORKSPACE root. - -set -euo pipefail - -bazel build docs:all - -for filename in bazel-bin/docs/*_gen.md; do - target_filename="$(echo $filename | sed -En "s/bazel-bin\/(.*)_gen.md/\1/p").md" - cp $filename $target_filename -done