diff --git a/docusaurus.config.ts b/docusaurus.config.ts index dc71a8acce..d8fee3cd89 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -7,7 +7,7 @@ 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 @@ -15,24 +15,13 @@ const config: Config = { 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 // pathname under which your site is served - // For GitHub pages deployment, it is often '//' 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"], @@ -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/' }, blog: false, theme: { @@ -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, @@ -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, }; diff --git a/scripts/build_whats_new.py b/scripts/build_whats_new.py new file mode 100644 index 0000000000..682c90014a --- /dev/null +++ b/scripts/build_whats_new.py @@ -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/whats-new.mdx`` + +* All other branches are written to: + + ``/versioned_docs/version-/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
(---) with MDX‑safe
+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}", + "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
so MDX doesn't choke.""" + md_body = md(html, strip=["hr"]) + md_body = HR_MARKDOWN.sub("
", 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"]) + +# ============================================================================ +# 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} [ ...]") + + 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() \ No newline at end of file diff --git a/scripts/deploy.ps1 b/scripts/deploy.ps1 new file mode 100644 index 0000000000..5b7ed17fc2 --- /dev/null +++ b/scripts/deploy.ps1 @@ -0,0 +1,150 @@ +<#! ----------------------------------------------------------------------------- +Deploy.ps1 – RavenDB Docs Deployment +----------------------------------- +Deploys the Docusaurus static site to an AWS S3 bucket and optionally +invalidates a CloudFront distribution. If the **-Versions** switch is used, the +script first regenerates the *What's New* Markdown for those RavenDB **versions** +(via `build_whats_new.py`) before building and publishing the site. + +Prerequisites +------------- +* **Node.js ≥ 18** (with npm) +* **Python ≥ 3** (`python` in PATH) – runs the changelog generator +* **AWS CLI v2** – credentials via `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, + `AWS_DEFAULT_REGION` (and optional `AWS_SESSION_TOKEN`) +* Environment variable **`RAVENDB_API_KEY`** set (only required when regenerating + *What's New* for specific versions) +* Project `package.json` includes `@docusaurus/core` and `@docusaurus/cli` + +Example +------- +```powershell +# RAVENDB_API_KEY must be set if you request specific versions +$env:RAVENDB_API_KEY = 'YOUR-API-KEY' + +pwsh deploy.ps1 \ + -S3BucketName my-docs-bucket \ + -CloudFrontDistributionId ABCD1234 \ + -Versions 6.0 6.2 7.0 7.1 8.0 +``` +------------------------------------------------------------------------------!#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true, HelpMessage = 'Target S3 bucket name')] + [string]$S3BucketName, + + [Parameter(HelpMessage = 'CloudFront distribution ID to invalidate (optional)')] + [string]$CloudFrontDistributionId, + + [Parameter(ValueFromRemainingArguments = $false, HelpMessage = "Versions to regenerate What's New for – e.g. 6.0 7.0 8.0")] + [string[]]$Versions = @() +) + +# --------------------------------------------------------------------------- +# Constants & helpers +# --------------------------------------------------------------------------- +$PythonWhatsNewPath = Join-Path $PSScriptRoot 'build_whats_new.py' # script lives alongside this file + +function ThrowIfEmpty { + param ( + [string]$Value, + [string]$Message + ) + if (-not $Value) { throw $Message } +} + +function Ensure-Dependencies { + Write-Host 'Verifying runtime dependencies…' -ForegroundColor Cyan + + foreach ($cmd in 'node','npm','python','aws') { + if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) { + throw "$cmd not found in PATH" + } + } + + $nodeVer = (node -v).Trim() + $npmVer = (npm --version).Trim() + $pythonVer = (python --version 2>&1).Trim() + $awsVer = (aws --version).Trim() + + Write-Host "Node.js $nodeVer | npm $npmVer | Python $pythonVer | AWS $awsVer" -ForegroundColor Gray +} + +function Process-Changelogs { + param ( + [string[]]$VersionsToBuild + ) + + if (-not $VersionsToBuild) { return } + + # Ensure API key exists before invoking Python helper + ThrowIfEmpty $Env:RAVENDB_API_KEY 'RAVENDB_API_KEY env var must be set when specifying versions' + + Write-Host "Generating *What's New* pages for versions: $($VersionsToBuild -join ', ')" -ForegroundColor Cyan + + & python $PythonWhatsNewPath @VersionsToBuild + if ($LASTEXITCODE) { throw 'build_whats_new.py failed' } +} + +# --------------------------------------------------------------------------- +# 0. Verify runtime dependencies +# --------------------------------------------------------------------------- +Ensure-Dependencies + +# --------------------------------------------------------------------------- +# 1. Validate AWS environment +# --------------------------------------------------------------------------- +ThrowIfEmpty $Env:AWS_ACCESS_KEY_ID 'AWS_ACCESS_KEY_ID not set' +ThrowIfEmpty $Env:AWS_SECRET_ACCESS_KEY 'AWS_SECRET_ACCESS_KEY not set' +ThrowIfEmpty $Env:AWS_DEFAULT_REGION 'AWS_DEFAULT_REGION not set' + +Write-Host "Region: '$Env:AWS_DEFAULT_REGION' | Bucket: '$S3BucketName'" -ForegroundColor Cyan + +# --------------------------------------------------------------------------- +# 2. Validate RAVENDB_API_KEY when versions are provided +# --------------------------------------------------------------------------- +if ($Versions.Count -gt 0) { + ThrowIfEmpty $Env:RAVENDB_API_KEY 'RAVENDB_API_KEY env var must be set when specifying versions' +} + +# --------------------------------------------------------------------------- +# 3. Regenerate What's New (if requested) +# --------------------------------------------------------------------------- +Process-Changelogs -VersionsToBuild $Versions + +# --------------------------------------------------------------------------- +# 4. Install JS dependencies & build site +# --------------------------------------------------------------------------- +Write-Host 'Installing JS dependencies (npm ci)…' -ForegroundColor Cyan +$env:NODE_OPTIONS = '--max-old-space-size=8192' + +if (-not (Test-Path package.json)) { throw 'package.json not found' } + +npm ci --no-audit --fund false +if ($LASTEXITCODE) { throw 'npm ci failed' } + +Write-Host "Running 'npx docusaurus build --dev'…" -ForegroundColor Gray +npx docusaurus build --dev +if ($LASTEXITCODE) { throw 'Docusaurus build failed' } + +$BuildDir = Join-Path $PSScriptRoot 'build' +if (-not (Test-Path $BuildDir)) { throw "Build folder not produced ($BuildDir)" } + +# --------------------------------------------------------------------------- +# 5. Sync build output to S3 +# --------------------------------------------------------------------------- +Write-Host "Syncing to s3://$S3BucketName/ …" -ForegroundColor Cyan +aws s3 sync $BuildDir "s3://$S3BucketName/" --delete +if ($LASTEXITCODE) { throw 'aws s3 sync failed' } + +# --------------------------------------------------------------------------- +# 6. Invalidate CloudFront (optional) +# --------------------------------------------------------------------------- +if ($CloudFrontDistributionId) { + Write-Host "Invalidating CloudFront distribution $CloudFrontDistributionId" -ForegroundColor Cyan + aws cloudfront create-invalidation --distribution-id $CloudFrontDistributionId --paths '/*' | Out-Null + if ($LASTEXITCODE) { throw 'CloudFront invalidation failed' } +} + +Write-Host 'Deployment completed successfully.' -ForegroundColor Green \ No newline at end of file diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000000..78b1dc99fb --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,6 @@ +# robots.txt for RavenDB Documentation +User-agent: * +Allow: / + +# Sitemaps +Sitemap: https://test.docs.ravendb.net/sitemap.xml \ No newline at end of file