| # 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, version): | |
| return _HTML_LINK_PATTERN.sub(r"\1/versions/{}/".format(version), content) | |
| def _fix_html_metadata(content, version): | |
| return content.replace("value=\"/_book.yaml\"", | |
| "value=\"/versions/{}/_book.yaml\"".format(version)) | |
| _MD_LINK_OR_IMAGE_PATTERN = re.compile( | |
| r"(\!?\[.*?\]\(({})?)(/.*?)\)".format(_BASE_URL)) | |
| def _fix_md_links_and_images(content, version): | |
| 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, version): | |
| return _MD_METADATA_PATTERN.sub(r"\1/versions/{}\2".format(version), content) | |
| _YAML_PATH_PATTERN = re.compile(r"((book_|image_)?path: ['\"]?)(/.*?)(['\"]?)$", | |
| re.MULTILINE) | |
| _YAML_IGNORE_LIST = frozenset( | |
| ["/", "/_project.yaml", "/versions/", "/versions/_toc.yaml"]) | |
| def _fix_yaml_paths(content, version): | |
| def sub(m): | |
| prefix, path, suffix = m.group(1, 3, 4) | |
| 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] | |
| _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, 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. | |
| 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, version) | |
| return new_content |