blob: e4571457ec77a3395a7d6dfee8c83666fe9d9c0c [file] [log] [blame]
# Lint as: python3
# pylint: disable=g-direct-third-party-import
# Copyright 2022 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.
"""Module for fixing links in Bazel release docs."""
import os
import re
_BASE_URL = "https://bazel.build"
# We need to use regular expressions here since HTML can be embedded in
# Markdown and Yaml, thus breaking XML parsers. Moreover, our use case is
# simple, so regex should work (tm).
_HTML_LINK_PATTERN = re.compile(
r"((href|src)\s*=\s*[\"']({})?)/".format(_BASE_URL))
def _fix_html_links(content, rel_path, version):
del rel_path # unused
return _HTML_LINK_PATTERN.sub(r"\1/versions/{}/".format(version), content)
def _fix_html_metadata(content, rel_path, version):
del rel_path # unused
return content.replace("value=\"/_book.yaml\"",
"value=\"/versions/{}/_book.yaml\"".format(version))
def _set_header_vars(content, rel_path, version):
return content.replace(
"""{% include "_buttons.html" %}""",
f"""{{% dynamic setvar version "{version}" %}}
{{% dynamic setvar original_path "/{os.path.splitext(rel_path)[0]}" %}}
{{% include "_buttons.html" %}}""",
)
_MD_LINK_OR_IMAGE_PATTERN = re.compile(
r"(\!?\[.*?\]\(({})?)(/.*?)\)".format(_BASE_URL))
def _fix_md_links_and_images(content, rel_path, version):
del rel_path # unused
return _MD_LINK_OR_IMAGE_PATTERN.sub(r"\1/versions/{}\3)".format(version),
content)
_MD_METADATA_PATTERN = re.compile(r"^(Book: )(/.+)$", re.MULTILINE)
def _fix_md_metadata(content, rel_path, version):
del rel_path # unused
return _MD_METADATA_PATTERN.sub(r"\1/versions/{}\2".format(version), content)
_YAML_PATH_PATTERN = re.compile(
r"(((book_|image_)?path|include): ['\"]?)(/.*?)(['\"]?)$", re.MULTILINE
)
_YAML_IGNORE_LIST = frozenset(
["/", "/_project.yaml", "/versions/", "/versions/_toc.yaml"])
def _fix_yaml_paths(content, rel_path, version):
del rel_path # unused
def sub(m):
prefix, path, suffix = m.group(1, 4, 5)
if path in _YAML_IGNORE_LIST:
return m.group(0)
return "{}/versions/{}{}{}".format(prefix, version, path, suffix)
return _YAML_PATH_PATTERN.sub(sub, content)
_PURE_HTML_FIXES = [_fix_html_links, _fix_html_metadata]
_PURE_MD_FIXES = [_fix_md_links_and_images, _fix_md_metadata, _set_header_vars]
_PURE_YAML_FIXES = [_fix_yaml_paths]
_FIXES = {
".html": _PURE_HTML_FIXES,
".md": _PURE_MD_FIXES + _PURE_HTML_FIXES,
".yaml": _PURE_YAML_FIXES + _PURE_HTML_FIXES,
}
def _get_fixes(path):
_, ext = os.path.splitext(path)
return _FIXES.get(ext)
def can_rewrite(path):
"""Returns whether links in this file can/should be rewritten.
Args:
path: Path of the file in question.
Returns:
True if the file can/should be rewritten.
"""
return bool(_get_fixes(path))
def rewrite_links(path, content, rel_path, version):
"""Rewrites links in the given file to point to versioned docs.
Args:
path: Absolute path of the file to be rewritten.
content: Content of said file, as text.
rel_path: Relative path of the file to be rewritten.
version: Version of the Bazel release that is being built.
Returns:
The rewritten content of the file, as text. Equal to `content`
if no links had to be rewritten.
"""
fixes = _get_fixes(path)
if not fixes:
raise ValueError(
"Cannot rewrite {} due to unsupported file type.".format(path))
new_content = content
for f in fixes:
new_content = f(new_content, rel_path, version)
return new_content