Skip to content

CXX-3097 add patch-apidocs-current-redirects.py #1238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 23, 2024
Merged
6 changes: 4 additions & 2 deletions Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ FULL_PATH_NAMES = YES
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.

STRIP_FROM_PATH = src/bsoncxx/include \
STRIP_FROM_PATH = . \
src/bsoncxx/include \
src/mongocxx/include

# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
Expand All @@ -190,7 +191,8 @@ STRIP_FROM_PATH = src/bsoncxx/include \
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.

STRIP_FROM_INC_PATH = src/bsoncxx/include \
STRIP_FROM_INC_PATH = . \
src/bsoncxx/include \
src/mongocxx/include

# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
Expand Down
3 changes: 2 additions & 1 deletion docs/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ theme = "mongodb"
canonifyurls = false
relativeurls = false
publishdir = "../build/hugo"
disableKinds = ["taxonomy", "term"]

[blackfriday]
plainIdAnchors = true
Expand All @@ -18,7 +19,7 @@ publishdir = "../build/hugo"

[[menu.main]]
name = "API Documentation"
pre = "<i class='fa fa-file-text-o'></i>"
pre = "<i class='fa fa-book'></i>"
weight = 90
identifier = "apiDocs"
url = "https://mongocxx.org/api/current"
Expand Down
4 changes: 2 additions & 2 deletions docs/themes/mongodb/theme.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ name = "MongoDB"
license = "Creative Commons Attribution NonCommercial ShareAlike 3.0 Unported"
licenselink = "http://creativecommons.org/licenses/by-nc-sa/3.0/"
description = "A standalone mongodb docs theme, for individual driver homepages"
homepage = "https://mongocxx.org/"
homepage = "https://mongocxx.org"

[author]
name = "MongoDB C++ driver authors"
homepage = "https://mongocxx.org/"
homepage = "https://mongocxx.org"

[original]
name = "MongoDB Docs"
Expand Down
5 changes: 3 additions & 2 deletions etc/deploy-to-ghpages.pl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ sub _pushd {

sub _hugo_rsync {
my $tmpdir = shift;
_try_run( qw{rsync -Cavz --delete --exclude=/api --exclude=/.git* --exclude=CNAME build/hugo/},
_try_run( qw{rsync -Cavz --delete --exclude=/api --exclude=/.git* --exclude=CNAME --exclude=sitemap.xml build/hugo/},
$tmpdir );
}

Expand All @@ -42,7 +42,8 @@ sub _doxygen_rsync {
"build/docs/api/", "$tmpdir/api/"
);
$ENV{APIDOCSPATH} = "$tmpdir/api";
_try_run(qw{etc/patch-apidocs-index-pages.py})
_try_run(qw{etc/patch-apidocs-index-pages.py});
_try_run(qw{etc/patch-apidocs-current-redirects.py});
}

sub main {
Expand Down
186 changes: 186 additions & 0 deletions etc/patch-apidocs-current-redirects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/env python3

# Copyright 2009-present MongoDB, Inc.
#
# 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.

"""
Patches HTML files within the latest API doc directory (under APIDOCSPATH) to
redirect users from `/api/current` to canonical URLs under `/api/mongocxx-X.Y.Z`.
"""

from concurrent.futures import ProcessPoolExecutor
from packaging.version import Version, InvalidVersion
from pathlib import Path
from typing import List, Tuple

import re
import os


def find_api_docs_path() -> str:
"""
Return an absolute path to the directory containing the API docs.
"""
api_docs_path: str | None = os.environ.get('APIDOCSPATH')
if not api_docs_path:
raise RuntimeError('APIDOCSPATH environment variable is not set!')

if not os.path.exists(api_docs_path):
raise RuntimeError('path to API docs does not exist!')

return os.path.abspath(api_docs_path)


def find_api_docs(api_docs_path: str) -> List[str]:
"""
Return a list of API doc directories by name.
"""
api_docs: List[str] = []
for dir in os.scandir(api_docs_path):
if dir.is_dir() and not dir.is_symlink():
api_docs.append(dir.name)

# Sort by legacy vs. modern, then by SemVer. Example:
# - legacy-0.1.0
# - legacy-0.2.0
# - legacy-0.10.0
# - mongocxx-3.1.0
# - mongocxx-3.2.0
# - mongocxx-3.10.0
# Skip directories with a version suffix, e.g. `mongocxx-1.2.3-rc0`.
def by_version(p: str) -> Tuple[bool, Version] | None:
is_legacy: bool = p.startswith('legacy-')
try:
version = p.removeprefix('legacy-') if is_legacy else p.removeprefix('mongocxx-')
if version.find('-') != -1:
print(f' - Skipping: {p}')
return None
return (not is_legacy, Version(version))
except InvalidVersion:
raise RuntimeError(f'unexpected API doc name "{p}": APIDOCSPATH may not be correct!') from None

api_docs = [doc for doc in api_docs if by_version(doc) is not None]
api_docs.sort(key=by_version)

return api_docs


def patch_redirect_current_pages(apidocspath, latest):
"""
Patch all HTML files under the latest API doc directory.
"""

pages: List[Path] = []

for (dirpath, _, filenames) in os.walk(os.path.join(apidocspath, latest)):
for filename in filenames:
page = Path(os.path.join(dirpath, filename))
if page.suffix == '.html':
pages.append(page)

futures = []

with ProcessPoolExecutor() as executor:
for page in pages:
futures.append(executor.submit(insert_current_redirect, apidocspath, page, latest))

for future in futures:
future.result()


def insert_current_redirect(apidocspath, page, latest):
"""
Insert a <link> and <script> at the end of the <head> section.
Skip modifying the document if the patch tag is found.
"""

path = str(Path(page).relative_to(os.path.join(apidocspath, latest)))

patch_tag = f'patch-apidocs-current-redirects: {latest}'

is_patched = re.compile(patch_tag)
end_of_head_re = re.compile(r'^(\s*)</head>$')

with open(page, "r+") as file:
lines = [line for line in file]

idx = None
indent = ''

for idx, line in enumerate(lines):
if is_patched.search(line):
# This file has already been patched.
return

m = end_of_head_re.match(line)
if m:
# Patched index.html has 1-space indentation. The rest have none.
indent = '' if m.group(1) == '' else ' '
end_of_head = idx
break

if idx is None:
raise RuntimeError(f'could not find end of `<head>` in {path}')

# Insert patch tag to avoid repeated patch of the same file.
lines.insert(end_of_head, indent + f'<!-- {patch_tag} -->\n')
end_of_head += 1

# Canonical URL. Inform search engines about the redirect.
lines.insert(
end_of_head,
indent + f'<link rel="canonical" href="https://mongocxx.org/api/{latest}/{path}"/>\n')
end_of_head += 1

# Redirect script. Avoid generating history for the `/current` page during the redirect.
script = ''
script += indent + '<script type="text/javascript">\n'
script += indent + 'if (window.location.pathname.startsWith("/api/current/")) {\n'
script += indent + ' window.location.replace(\n'
script += indent + f' window.location.href.replace("/api/current/", "/api/{latest}/")\n'
script += indent + ' )\n'
script += indent + '}\n'
script += indent + '</script>\n'
lines.insert(end_of_head, script)
end_of_head += 1

file.seek(0)
for line in lines:
file.write(line)
file.truncate()


def main():
api_docs_path: str = find_api_docs_path()

print(f'Patching API docs in: {api_docs_path}')

print('Finding API docs...')
api_docs = find_api_docs(api_docs_path)
if len(api_docs) == 0:
raise RuntimeError(f'no API docs found: APIDOCSPATH may not be correct!')
print('Finding API docs... done.')

print(f' - Found {len(api_docs)} API docs: {api_docs[0]} ... {api_docs[-1]}')

latest_doc = api_docs[-1]
print(f' - Using {latest_doc} as the latest API doc.')

print(f'Patching latest API doc pages to redirect from /current to /{latest_doc}...')
patch_redirect_current_pages(api_docs_path, latest_doc)
print(f'Patching latest API doc pages to redirect from /current to /{latest_doc}... done.')


if __name__ == '__main__':
main()
23 changes: 20 additions & 3 deletions etc/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ These commands will update the `gh-pages` branch and push the changes to the rem
> [!WARNING]
> Build and release artifacts may still be present in the repository after this step. Do not accidentally commit these files into the repository in the following steps!

### Update Symlinks
### Update gh-pages

> [!IMPORTANT]
> Symlink updates only apply to stable releases! Release candidates and other unstable releases do not require updating symlinks.
Expand All @@ -673,10 +673,27 @@ current -> mongocxx-v3
mongocxx-v3 -> mongocxx-X.Y.Z
```

Commit and push this change to the `gh-pages` branch:
Add a new entry to the `sitemap_index.xml` file referencing the sitemap for `api/mongocxx-X.Y.Z`.
Set `<lastmod>` for both the new entry and the `/current` sitemap entry to the current date:

```xml
...
<!-- API Documentation Pages. -->
<sitemap>
<loc>https://mongocxx.org/api/current/sitemap.xml</loc>
<lastmod>YYYY-MM-DD</lastmod>
</sitemap>
<sitemap>
<loc>https://mongocxx.org/api/mongocxx-X.Y.Z/sitemap.xml</loc>
<lastmod>YYYY-MM-DD</lastmod>
</sitemap>
...
```

Commit and push these change to the `gh-pages` branch:

```bash
git commit -m "Update symlink for rX.Y.Z"
git commit -m "Update symlink and sitemap for rX.Y.Z"
```

Wait for [GitHub Actions](https://github.com/mongodb/mongo-cxx-driver/actions) to finish deploying the updated pages.
Expand Down