Skip to content

RDoc-3361 Added deployment script #2068

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 5 commits into from
Aug 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,21 @@ import type * as Preset from "@docusaurus/preset-classic";
const config: Config = {
title: "RavenDB Documentation",
tagline:
"Everything you need to know about our product, from getting started to advanced features.",
"High-performance NoSQL database that just works.",
favicon: "img/favicon.ico",

// Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future
future: {
v4: true, // Improve compatibility with the upcoming Docusaurus v4
},

// Set the production url of your site here
url: "https://test.docs.ravendb.net/",
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: "/",

// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: "facebook", // Usually your GitHub org/user name.
projectName: "docusaurus", // Usually your repo name.

onBrokenLinks: "ignore",
onBrokenMarkdownLinks: "ignore",
onBrokenAnchors: "ignore",

// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
// may want to replace "en" with "zh-Hans".
i18n: {
defaultLocale: "en",
locales: ["en"],
Expand All @@ -53,10 +42,9 @@ const config: Config = {
path: "/7.1"
}
}
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
// editUrl:
// 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
// 'https://github.com/ravendb/docs/new-docs/main/'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remember to change it while deploying @Lwiel

},
blog: false,
theme: {
Expand Down Expand Up @@ -138,6 +126,16 @@ const config: Config = {
},
},
],
metadata: [
{
name: 'keywords',
content: 'nosql, document database'
},
{
name: 'description',
content: 'Official RavenDB documentation. Learn installation, querying, indexing, scaling, security, and every advanced feature of the fully ACID NoSQL database that combines performance with ease of use.'
}
],
colorMode: {
defaultMode: "dark",
disableSwitch: false,
Expand Down Expand Up @@ -236,8 +234,6 @@ const config: Config = {

// Optional: whether the insights feature is enabled or not on Docsearch (`false` by default)
insights: false,

//... other Algolia params
},
} satisfies Preset.ThemeConfig,
};
Expand Down
194 changes: 194 additions & 0 deletions scripts/build_whats_new.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""build_whats_new.py – regenerate RavenDB *What's New* pages

The script lives in the project's **/scripts** folder, therefore the Docusaurus
root is assumed to be its parent directory (``../``).

What the script does
--------------------
1. **Downloads** changelog entries for one or more RavenDB branches via the
public Documentation API.
2. **Converts** each entry's HTML body to Markdown using *markdownify*.
3. **Sorts** the entries strictly by their ``buildDate`` field (newest → oldest).
4. **Writes** them to ``whats-new.mdx`` files with front‑matter already in place.

File locations
--------------
* The branch that the API reports as **latest** is considered *primary* and
written to:

``<docs_root>/docs/whats-new.mdx``

* All other branches are written to:

``<docs_root>/versioned_docs/version-<branch>/whats-new.mdx``

Environment variable
--------------------
Set your RavenDB docs API key in ``RAVENDB_API_KEY`` before running.

Examples
--------
# Rebuild for the currently latest branch only
python build_whats_new.py 6.2

# Rebuild for multiple branches
python build_whats_new.py 6.2 6.0 5.4
"""

from __future__ import annotations

import os
import re
import sys
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Any

import requests
from markdownify import markdownify as md

# ============================================================================
# Configuration & paths
# ============================================================================

API_KEY: str | None = os.environ.get("RAVENDB_API_KEY")
API_BASE_URL = "https://api.web.ravendb.net/api/v1/documentation/whats-new"

if not API_KEY:
raise EnvironmentError("Environment variable 'RAVENDB_API_KEY' is not set.")

SCRIPT_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = SCRIPT_DIR.parent # «../» relative to /scripts

# Docusaurus front‑matter block that prefixes every generated MDX file
FRONT_MATTER = (
"---\n"
'title: "What\'s New"\n'
"breadcrumbs: false\n"
"pagination_next: null\n"
"pagination_prev: null\n"
"hide_table_of_contents: true\n"
"---\n\n"
)

# Regex that replaces markdownify's three‑dash <hr> (---) with MDX‑safe <hr/>
HR_MARKDOWN = re.compile(r"^-{3,}$", re.MULTILINE)

# Date format used by the API – helper for ``datetime.strptime``
API_DATE_FMT = "%m/%d/%Y"

# ============================================================================
# REST helpers
# ============================================================================

def get_api_page(branch: str, page: int = 1) -> Dict[str, Any]:
"""Return a single paginated payload from the Documentation API."""
response = requests.get(
API_BASE_URL,
headers={
"Authorization": f"Bearer {API_KEY}",
Copy link
Member

@gregolsky gregolsky Aug 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, you won't be able to do it like this I'm afraid. This token I supplied you with is short-lived and to get a new one, you'd need some more details and ceremony. To get it going let's download these items, make what's new files and push it to the repo for the time being.

This one is a no-go in the current shape.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we can make it work. @poissoncorp Please arrange a short one with @lukaszdobrzynski - we'd need a new secret.

"Accept": "application/json",
},
params={"version": branch, "page": page},
timeout=20,
)

if response.status_code == 404:
sys.exit(f"Branch '{branch}' not found – check availableVersionsAndKeys in the API.")

response.raise_for_status()
return response.json()


def api_latest_branch() -> str:
"""The newest branch announced by the API (e.g. "6.2")."""
probe = get_api_page("6.0", 1) # 6.0 endpoint is guaranteed to exist
return sorted(probe["availableVersionsAndKeys"])[-1]


def fetch_branch_entries(branch: str) -> List[Dict[str, Any]]:
"""Download *all* changelog entries for a given branch.

The API is paginated – this helper stitches the pages into one list.
"""
first_page = get_api_page(branch, 1)
entries: List[Dict[str, Any]] = first_page["entries"]

for page_no in range(2, first_page["totalPages"] + 1):
entries.extend(get_api_page(branch, page_no)["entries"])

return entries

# ============================================================================
# Conversion helpers
# ============================================================================

def convert_html_to_markdown(html: str) -> str:
"""Convert HTML → Markdown and patch <hr> so MDX doesn't choke."""
md_body = md(html, strip=["hr"])
md_body = HR_MARKDOWN.sub("<hr/>", md_body)
return md_body.rstrip() + "\n\n"


def mdx_heading(entry: Dict[str, Any]) -> str:
"""Create a level‑2 MDX heading from an API entry."""
date_str = datetime.strptime(entry["buildDate"], API_DATE_FMT).strftime("%Y/%m/%d")
return f"## {entry['version']} - {date_str}\n\n"


def mdx_block(entry: Dict[str, Any]) -> str:
"""Full MDX chunk for a single changelog entry (heading + body)."""
return mdx_heading(entry) + convert_html_to_markdown(entry["changelogHtml"])
Copy link
Member

@gregolsky gregolsky Aug 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can talk with the API team to add us source markdown field, which won't feel then like getting strawberries out of a milkshake. cc @lukaszdobrzynski

However we can go live with this, not a blocker.


# ============================================================================
# Filesystem helpers
# ============================================================================

def output_path_for(branch: str, is_primary: bool) -> Path:
"""Return where the *whats‑new.mdx* for *branch* should live."""
# We only need major.minor for the directory name – e.g. "6.2.1" → "6.2"
major_minor = ".".join(branch.split(".")[:2])

if is_primary:
return PROJECT_ROOT / "docs" / "whats-new.mdx"

return PROJECT_ROOT / "versioned_docs" / f"version-{major_minor}" / "whats-new.mdx"


def write_whats_new_file(destination: Path, entries: List[Dict[str, Any]]) -> None:
"""Write an MDX file sorted by *buildDate* (newest first)."""
destination.parent.mkdir(parents=True, exist_ok=True)

entries.sort(
key=lambda e: datetime.strptime(e["buildDate"], API_DATE_FMT),
reverse=True, # newest → oldest
)

body = "".join(mdx_block(e) for e in entries)
destination.write_text(FRONT_MATTER + body, encoding="utf-8")

# ============================================================================
# Command‑line interface
# ============================================================================

def main() -> None:
if len(sys.argv) < 2:
script_name = Path(sys.argv[0]).name
sys.exit(f"Usage: python {script_name} <BRANCH> [<BRANCH> ...]")

primary_branch = api_latest_branch()
requested_branches = sys.argv[1:]

for branch in requested_branches:
is_primary = branch == primary_branch
changelog_entries = fetch_branch_entries(branch)
target_file = output_path_for(branch, is_primary)
write_whats_new_file(target_file, changelog_entries)
print(f"✅ Wrote {target_file.relative_to(PROJECT_ROOT)}")

print("🏁 Finished.")


if __name__ == "__main__":
main()
Loading