Skip to content

Commit

Permalink
Automatically update version indicator
Browse files Browse the repository at this point in the history
  • Loading branch information
fmeum committed Jun 18, 2024
1 parent 418bdc0 commit d976428
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 12 deletions.
14 changes: 11 additions & 3 deletions scripts/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ genrule(
name = "gen_release_docs",
srcs = [
":new_toc.yaml",
":new_buttons.html",
"//site/en:docs",
"//src/main/java/com/google/devtools/build/lib:reference-docs.zip",
],
Expand All @@ -16,6 +17,7 @@ genrule(
cmd = "$(location :create_release_docs)" +
" --version=" + BUILD_SCM_REV_CMD +
" --toc_path=$(location :new_toc.yaml)" +
" --buttons_path=$(location :new_buttons.html)" +
" --narrative_docs_path=$(location //site/en:docs)" +
" --reference_docs_path=$(location //src/main/java/com/google/devtools/build/lib:reference-docs.zip)" +
" --output_path=$(OUTS)",
Expand All @@ -30,12 +32,18 @@ genrule(
name = "gen_new_toc",
srcs = [
"//site/en:versions/_toc.yaml",
"//site/en:_buttons.html",
],
outs = [
"new_toc.yaml",
"new_buttons.html",
],
outs = ["new_toc.yaml"],
cmd = "$(location //src/main/java/com/google/devtools/build/docgen/release:toc_updater)" +
" -i $(location //site/en:versions/_toc.yaml)" +
" -o $(OUTS)" +
" -v " + BUILD_SCM_REV_CMD,
" -o $(location new_toc.yaml)" +
" -v " + BUILD_SCM_REV_CMD +
" --version_indicator_input=$(location //site/en:_buttons.html)" +
" --version_indicator_output=$(location new_buttons.html)",
stamp = 1,
tools = [
"//src/main/java/com/google/devtools/build/docgen/release:toc_updater",
Expand Down
14 changes: 13 additions & 1 deletion scripts/docs/create_release_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
None,
"Path to the _toc.yaml file that contains the table of contents for the versions menu.",
)
flags.DEFINE_string(
"buttons_path",
None,
"Path to the _buttons.html file that contains the version indicator.",
)
flags.DEFINE_string(
"narrative_docs_path",
None,
Expand Down Expand Up @@ -70,14 +75,16 @@ def validate_flag(name):
exit(1)


def create_docs_tree(version, toc_path, narrative_docs_path,
def create_docs_tree(version, toc_path, buttons_path, narrative_docs_path,
reference_docs_path):
"""Creates a directory tree containing the docs for the Bazel version.
Args:
version: Version of this Bazel release.
toc_path: Absolute path to the _toc.yaml file that lists the most recent
Bazel versions.
buttons_path: Absolute path of the _buttons.html file that contains the
version indicator.
narrative_docs_path: Absolute path of an archive that contains the narrative
documentation (can be .zip or .tar).
reference_docs_path: Absolute path of an archive that contains the reference
Expand All @@ -101,6 +108,10 @@ def create_docs_tree(version, toc_path, narrative_docs_path,
try_extract(narrative_docs_path, release_dir)
try_extract(reference_docs_path, release_dir)

buttons_dest_path = os.path.join(release_dir, "_buttons.html")
os.remove(buttons_dest_path)
shutil.copyfile(buttons_path, buttons_dest_path)

return root_dir, toc_dest_path, release_dir


Expand Down Expand Up @@ -179,6 +190,7 @@ def main(unused_argv):
root_dir, toc_path, release_dir = create_docs_tree(
version=version,
toc_path=validate_flag("toc_path"),
buttons_path=validate_flag("buttons_path"),
narrative_docs_path=validate_flag("narrative_docs_path"),
reference_docs_path=validate_flag("reference_docs_path"),
)
Expand Down
1 change: 1 addition & 0 deletions site/en/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exports_files(
[
"docs/user-manual.md",
"versions/_toc.yaml",
"_buttons.html",
],
visibility = [
"//scripts/docs:__pkg__",
Expand Down
5 changes: 4 additions & 1 deletion site/en/_buttons.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
{% dynamic if not setvar.original_path %}
{% dynamic setvar original_path %}{% dynamic print request.path %}{% dynamic endsetvar %}
{% dynamic endif %}
<span style="float: right; line-height: 36px">
<span class="bazel-version-indicator">
{% dynamic if not setvar.version %}
<strong>Nightly</strong>
{% dynamic else %}
<a href="{% dynamic print setvar.original_path %}">Nightly</a>
{% dynamic endif %}
<!-- The lines below are updated by //scripts/docs:gen_new_toc -->
<!-- BEGIN_VERSION_INDICATOR -->
·
{% dynamic if setvar.version == "7.2.0" %}
<strong>7.2</strong>
Expand Down Expand Up @@ -49,5 +51,6 @@
{% dynamic else %}
<a href="/versions/6.4.0{% dynamic print setvar.original_path %}">6.4</a>
{% dynamic endif %}
<!-- END_VERSION_INDICATOR -->
</span>
</p>
5 changes: 5 additions & 0 deletions site/en/bazel.css
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,8 @@
margin-inline-start: 8px;
vertical-align: text-bottom;
}

.bazel-version-indicator {
float: right;
line-height: 36px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ public class TableOfContentsOptions extends OptionsBase {
help = "Path of the YAML file where the new TOC should be written to.")
public String outputPath;

@Option(
name = "version_indicator_input",
defaultValue = "",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Path of the file containing the version indicator.")
public String versionIndicatorInputPath;

@Option(
name = "version_indicator_output",
defaultValue = "",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help = "Path of the file where the version indicator should be written.")
public String versionIndicatorOutputPath;

@Option(
name = "version",
abbrev = 'v',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@
package com.google.devtools.build.docgen.release;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;

import com.google.common.flogger.GoogleLogger;
import com.google.devtools.common.options.OptionsParser;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

Expand All @@ -33,6 +37,20 @@ public class TableOfContentsUpdater {

private static final String VERSION_ROOT = "/versions/";

private static final String VERSION_INDICATOR_START = "<!-- BEGIN_VERSION_INDICATOR -->";

private static final String VERSION_INDICATOR_END = "<!-- END_VERSION_INDICATOR -->";

private static final String VERSION_INDICATOR_TEMPLATE =
"""
·
{% dynamic if setvar.version == "{canonical_version}" %}
<strong>{pretty_version}</strong>
{% dynamic else %}
<a href="{version_root}{canonical_version}/{% dynamic print setvar.original_path %}">{pretty_version}</a>
{% dynamic endif %}
""";

private TableOfContentsUpdater() {}

public static void main(String[] args) {
Expand All @@ -52,23 +70,43 @@ public static void main(String[] args) {
}

Yaml yaml = new Yaml(getYamlOptions());
List<String> versions;
try (FileInputStream fis = new FileInputStream(options.inputPath)) {
Object data = yaml.load(fis);
update(data, options.version, options.maxReleases);
versions = updateTocAndGetVersions(data, options.version, options.maxReleases);
yaml.dump(data, new OutputStreamWriter(new FileOutputStream(options.outputPath), UTF_8));
} catch (Throwable t) {
System.err.printf("ERROR: %s\n", t.getMessage());
logger.atSevere().withCause(t).log(
"Failed to transform TOC from %s to %s", options.inputPath, options.outputPath);
Runtime.getRuntime().exit(1);
throw new IllegalStateException("Not reached");
}

if (!options.versionIndicatorInputPath.isEmpty()) {
try {
Files.writeString(
Path.of(options.versionIndicatorOutputPath),
makeUpdatedVersionIndicator(
Files.readString(Path.of(options.versionIndicatorInputPath)), versions));
} catch (Throwable t) {
System.err.printf("ERROR: %s\n", t.getMessage());
logger.atSevere().withCause(t).log(
"Failed to update version indicator from %s to %s",
options.versionIndicatorInputPath, options.versionIndicatorOutputPath);
Runtime.getRuntime().exit(1);
}
}
}

private static void printUsage() {
System.err.println(
"Usage: toc-updater -i src_toc_path -o dest_toc_path -v version [-m max_releases] [-h]\n\n"
+ "Reads the input TOC, adds an entry for the specified version and saves the new TOC"
+ " at the specified location.\n");
"""
Usage: toc-updater -i src_toc_path -o dest_toc_path -v version [-m max_releases] [-h] [--version_indicator_input path --version_indicator_output path]
Reads the input TOC, adds an entry for the specified version and saves the new TOC\
at the specified location.
""");
}

private static DumperOptions getYamlOptions() {
Expand All @@ -79,10 +117,11 @@ private static DumperOptions getYamlOptions() {
return opts;
}

private static void update(Object data, String version, int maxReleases) {
private static List<String> updateTocAndGetVersions(
Object data, String version, int maxReleases) {
@SuppressWarnings("unchecked") // yaml deserialization
Map<String, List<Map<String, String>>> m = (Map<String, List<Map<String, String>>>) data;
List<Map<String, String>> toc = (List<Map<String, String>>) m.get("toc");
List<Map<String, String>> toc = m.get("toc");
if (toc == null) {
throw new IllegalStateException("Missing 'toc' element.");
}
Expand All @@ -91,9 +130,53 @@ private static void update(Object data, String version, int maxReleases) {
newEntry.put("path", String.format("%s%s", VERSION_ROOT, version));
newEntry.put("label", version);

toc.add(0, newEntry);
toc.addFirst(newEntry);
if (toc.size() > maxReleases) {
m.put("toc", toc.subList(0, maxReleases));
}

return m.get("toc").stream()
// Exclude legacy doc versions.
.filter(e -> e.get("path").startsWith(VERSION_ROOT))
.map(e -> e.get("label"))
.map(TableOfContentsUpdater::canonicalizeVersion)
.toList();
}

private static String makeUpdatedVersionIndicator(
String oldVersionIndicator, List<String> versions) {
int beginPos = oldVersionIndicator.indexOf(VERSION_INDICATOR_START);
int endPos = oldVersionIndicator.indexOf(VERSION_INDICATOR_END);
if (beginPos == -1 || endPos == -1) {
throw new IllegalStateException("Version indicator markers not found.");
}
// Include the line terminator.
String prefix =
oldVersionIndicator.substring(0, beginPos + VERSION_INDICATOR_START.length() + 1);
String suffix = oldVersionIndicator.substring(endPos);
return versions.stream()
.map(
version ->
VERSION_INDICATOR_TEMPLATE
.replace("{canonical_version}", version)
.replace("{pretty_version}", prettifyVersion(version))
.replace("{version_root}", VERSION_ROOT))
.collect(joining("", prefix, suffix));
}

private static String canonicalizeVersion(String version) {
if (version.split(Pattern.quote(".")).length < 3) {
return version + ".0";
} else {
return version;
}
}

private static String prettifyVersion(String version) {
if (version.endsWith(".0")) {
return version.substring(0, version.length() - 2);
} else {
return version;
}
}
}

0 comments on commit d976428

Please sign in to comment.