diff --git a/.github/workflows/build-check.yml b/.github/workflows/build-check.yml index 1b5b62a8b53ed..8e025bb56868a 100644 --- a/.github/workflows/build-check.yml +++ b/.github/workflows/build-check.yml @@ -70,11 +70,115 @@ jobs: exit 1 fi + - name: Detect changed doc versions and locales + id: detect + run: | + BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD) + CHANGED_FILES=$(git diff --name-only "$BASE_SHA" HEAD) + + echo "=== Changed files ===" + echo "$CHANGED_FILES" + + VERSIONS="" + LOCALES="en" + NEED_FULL_BUILD="false" + + # Check each changed file and map to doc version + while IFS= read -r file; do + case "$file" in + # English current (dev) docs + docs/*) + VERSIONS="current,$VERSIONS" + ;; + # English versioned docs + versioned_docs/version-*/*) + ver=$(echo "$file" | sed -n 's|versioned_docs/version-\([^/]*\)/.*|\1|p') + VERSIONS="${ver},$VERSIONS" + ;; + # Chinese current docs + i18n/zh-CN/docusaurus-plugin-content-docs/current/*) + VERSIONS="current,$VERSIONS" + LOCALES="en,zh-CN" + ;; + # Chinese versioned docs + i18n/zh-CN/docusaurus-plugin-content-docs/version-*/*) + ver=$(echo "$file" | sed -n 's|i18n/zh-CN/docusaurus-plugin-content-docs/version-\([^/]*\)/.*|\1|p') + VERSIONS="${ver},$VERSIONS" + LOCALES="en,zh-CN" + ;; + # Chinese community docs + i18n/zh-CN/docusaurus-plugin-content-docs-community/*|i18n/zh-CN/code.json) + LOCALES="en,zh-CN" + ;; + # Sidebar for current (dev) version + sidebars.ts) + VERSIONS="current,$VERSIONS" + ;; + # Versioned sidebars: extract version from filename + # e.g. versioned_sidebars/version-4.x-sidebars.json → 4.x + versioned_sidebars/version-*-sidebars.json) + ver=$(echo "$file" | sed -n 's|versioned_sidebars/version-\(.*\)-sidebars\.json|\1|p') + VERSIONS="${ver},$VERSIONS" + ;; + # Blog and community are independent plugins, not + # controlled by DOCS_VERSIONS. They are always built + # regardless of version filtering. + blog/*|community/*) + NEED_BUILD="true" + ;; + # Config, source code, or other structural changes + # require a full build to validate + sidebarsCommunity.json|docusaurus.config.js|src/*|static/*|config/*|package.json|yarn.lock|tailwind.config.js) + NEED_FULL_BUILD="true" + ;; + esac + done <<< "$CHANGED_FILES" + + # Deduplicate versions + if [ "$NEED_FULL_BUILD" = "true" ]; then + # Structural changes: build all active versions + DOCS_VERSIONS="" + echo "Structural changes detected, will build ALL versions." + elif [ -n "$VERSIONS" ]; then + # Only doc content changes: build only affected versions + DOCS_VERSIONS=$(echo "$VERSIONS" | tr ',' '\n' | sort -u | grep -v '^$' | tr '\n' ',' | sed 's/,$//') + echo "Doc-only changes detected for versions: $DOCS_VERSIONS" + else + # No versioned doc changes (e.g., only blog, community, or scripts). + # Blog and community plugins are always compiled by Docusaurus + # regardless of DOCS_VERSIONS, so we just set the minimal docs + # scope to keep the build fast. + DOCS_VERSIONS="current" + echo "No doc version changes detected, doing minimal build with 'current' only." + echo "(Blog and community plugins are always built regardless.)" + fi + + # Determine locales for the build command + LOCALE_ARGS="" + IFS=',' read -ra LOCALE_ARR <<< "$LOCALES" + for locale in "${LOCALE_ARR[@]}"; do + LOCALE_ARGS="$LOCALE_ARGS --locale $locale" + done + + echo "docs_versions=$DOCS_VERSIONS" >> "$GITHUB_OUTPUT" + echo "locale_args=$LOCALE_ARGS" >> "$GITHUB_OUTPUT" + echo "need_full_build=$NEED_FULL_BUILD" >> "$GITHUB_OUTPUT" + + echo "" + echo "=== Build plan ===" + echo " DOCS_VERSIONS: ${DOCS_VERSIONS:-all}" + echo " LOCALE_ARGS: $LOCALE_ARGS" + echo " NEED_FULL_BUILD: $NEED_FULL_BUILD" + - name: Build + env: + DOCS_VERSIONS: ${{ steps.detect.outputs.docs_versions }} run: | npm install -g yarn yarn cache clean export NODE_OPTIONS=--max-old-space-size=8192 yarn - PWA_SERVICE_WORKER_URL=https://doris.apache.org/sw.js yarn docusaurus build --locale en --locale zh-CN + + echo "Building with DOCS_VERSIONS=${DOCS_VERSIONS:-all}" + PWA_SERVICE_WORKER_URL=https://doris.apache.org/sw.js yarn docusaurus build ${{ steps.detect.outputs.locale_args }} rm -rf build diff --git a/README.md b/README.md index 9d862278e16e6..3be719e7d83e1 100644 --- a/README.md +++ b/README.md @@ -17,344 +17,129 @@ specific language governing permissions and limitations under the License. --> -# Doris document website +# Apache Doris Website -This repo is for [Apache Doris Website](https://doris.apache.org) +Source code for [doris.apache.org](https://doris.apache.org), built with [Docusaurus 3](https://docusaurus.io/). -And it use Github Action to automatically sync content from [Apache Doris Code Repo](https://github.com/apache/doris) +## Quick Start -There are 2 Github Actions: +### Prerequisites -1. cron-deploy-website.yml - - It will sync at 01:00 AM everyday from Doris's master branch. - -2. manual-deploy-website.yml - - It can only be triggered manually, and you can specify the branch name you want to sync. - -## View the website - -To view the website, navigate to -[https://doris.apache.org](https://doris.apache.org) - -## Run & Build Website - -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. - -### Installation - -``` -$ yarn -``` +- Node.js >= 18 +- Yarn ### Local Development -``` -$ yarn start -``` +Use `local_dev.sh` to run the site locally. It handles dependency installation, version filtering, and memory settings automatically. -This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. +```bash +# Start English dev server (default: only 'current' version, 2 GB memory) +./local_dev.sh -### Build +# Start Chinese dev server +./local_dev.sh start-zh -``` -$ yarn build -``` +# Start on a custom port +./local_dev.sh start --port 8080 -This command generates static content into the `build` directory and can be served using any static contents hosting service. +# Build a specific doc version +./local_dev.sh start --versions "4.x" -### Deployment +# Build English docs (production build) +./local_dev.sh build -Using SSH: +# Build all locales (en + zh-CN) +./local_dev.sh build-all +# Clean build artifacts and caches +./local_dev.sh clean ``` -$ USE_SSH=true yarn deploy -``` - -Not using SSH: - -``` -$ GIT_USER= yarn deploy -``` - -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. -# Modify the documentation +Run `./local_dev.sh help` for all available commands and options. -For how to submit pull requests, please refer to +### Production Build -- [How to Contribute](https://doris.apache.org/zh-CN/community/how-to-contribute/contribute-to-doris) - -- [How to contribute docs](https://doris.apache.org/community/how-to-contribute/contribute-doc) - -- [Docs Format Specification](https://doris.apache.org/community/how-to-contribute/docs-format-specification) - -## Doris Website Directory Structure - -```Plain -. -├── blog -│ ├── 1.1 Release.md -│ ├── Annoucing.md -│ ├── jd.md -│ ├── meituan.md -│ ├── release-note-0.15.0.md -│ ├── release-note-1.0.0.md -│ └── xiaomi.md -├── community -│ ├── design -│ │ ├── spark_load.md -│ │ ├── doris_storage_optimization.md -│ │ ├── grouping_sets_design.md -│ │ └── metadata-design.md -│ ├── ...... -├── docs -│ ├── admin-manual -│ │ ├── cluster-management -│ │ ├── config -│ │ ├── data-admin -│ │ ├── http-actions -│ │ ├── maint-monitor -│ │ ├── privilege-ldap -│ │ ├── multi-tenant.md -│ │ ├── optimization.md -│ │ ├── query-profile.md -│ │ └── sql-interception.md -│ │ └── workload-group.md -│ ├── ...... -├── i18n -│ └── zh-CN -│ ├── docusaurus-plugin-content-docs -│ │ ├── current -│ │ ├── version-1.2 -│ │ ├── version-2.0 -│ │ ├── version-2.1 -│ │ ├── current.json -│ │ ├── version-1.2.json -│ │ ├── version-2.0.json -│ │ ├── version-2.1.json -│ ├── docusaurus-plugin-content-docs-community -│ └── local_build_docs.sh -├── src -│ ├── components -│ │ ├── Icons -│ │ ├── More -│ │ ├── PageBanner -│ │ └── PageColumn -│ ├── ...... -├── static -│ ├── images -│ │ ├── Bloom_filter.svg.png -│ │ ├── ..... -│ └── js -│ └── redirect.js -├── versioned_docs -│ ├── version-1.2 -│ │ ├── admin-manual -│ │ ├── advanced -│ │ ├── benchmark -│ │ ├── data-operate -│ │ ├── data-table -│ │ ├── ecosystem -│ │ ├── faq -│ │ ├── get-starting -│ │ ├── install -│ │ ├── lakehouse -│ │ ├── query-acceleration -│ │ ├── releasenotes -│ │ └── sql-manual -│ └── version-2.0 -│ ├── admin-manual -│ ├── benchmark -│ ├── data-operate -│ ├── db-connect -│ ├── ecosystem -│ ├── faq -│ ├── get-starting -│ ├── install -│ ├── lakehouse -│ ├── query -│ ├── releasenotes -│ ├── sql-manual -│ └── table-design -└── version-2.1 -│ ├── admin-manual -│ ├── advanced -│ ├── benchmark -│ ├── data-operate -│ ├── data-table -│ ├── ecosystem -│ ├── faq -│ ├── get-starting -│ ├── install -│ ├── lakehouse -│ ├── query-acceleration -│ ├── releasenotes -│ └── sql-manual -├── versioned_sidebars -│ ├── version-1.2-sidebars.json -│ └── version-2.0-sidebars.json -│ └── version-2.1-sidebars.json -├── babel.config.js -├── build.sh -├── buildVersions.sh -├── docusaurus.config.js -├── package.json -├── README.md -├── sidebars.json -├── sidebarsCommunity.json -├── tree.out -├── tsconfig.json -├── versions.json +```bash +export NODE_OPTIONS=--max-old-space-size=8192 +yarn install +yarn docusaurus build --locale en --locale zh-CN ``` -The following describes the directory structure of the Doris Website site so that users can easily find the corresponding directory and submit changes. - -### 01 Blog Directory - -The blog directory is located at `/blog`. All Blog Markdown should be placed in that directory. - -If you would like to share your technical insights, welcome to directly submitting a Blog PR or contacting dev@doris.apache.org. - -### 02 Docs Directory - -Here is the list of files if you need to submit docs changes: - -1. **Markdown Files:** When you want to modify existing content or add new documents, you need to place them to the respective folders and both update Master branch and Version docs (2.1/2.0/1.2) . -2. **Sidebar Files:** These files control the directory structures. When adding new files or new directory, you should also update relative path in sidebar files that ensure the new document is displayed correctly in directory. Currently, Master branch and other versions have separate sidebar files, including `sidebar.json, version-2.0-sidebars.json, and version-2.1-sidebars.json`. +The output is generated in the `build/` directory. -Please make sure to update all the necessary files accordingly when modifying existing document content, adding new documents, or adding new directory sections. +## Directory Structure -The following are the detailed steps for explaining how and where modify the docs: - -**Updating Latest Version (Master Branch)** - -**1. Update content** - -This version is modified in the `/docs` directory - -```Plain -. -├── docs -│ ├── admin-manual -│ ├── ...... ``` - -**2. Update sidebar** - -The docs directory structure of the latest version is edited by `sidebar.json`. - -```Plain . -├── docs -│ ├── admin-manua -│ ├── ...... -├── i18n -├── src -├── static -├── versioned_docs -├── versioned_sidebars -├── sidebars.json +├── docs/ # Current (dev) version docs (English) +├── versioned_docs/ +│ ├── version-4.x/ # 4.x version docs (English) +│ ├── version-3.x/ # 3.x version docs (English) +│ └── version-2.1/ # 2.1 version docs (English) +├── i18n/zh-CN/ +│ └── docusaurus-plugin-content-docs/ +│ ├── current/ # Current (dev) version docs (Chinese) +│ ├── version-4.x/ # 4.x version docs (Chinese) +│ ├── version-3.x/ # 3.x version docs (Chinese) +│ └── version-2.1/ # 2.1 version docs (Chinese) +├── blog/ # Blog posts +├── community/ # Community docs +├── src/ # React components and pages +├── static/ # Static assets (images, JS, CSS) +├── sidebars.ts # Sidebar for current (dev) docs +├── versioned_sidebars/ # Sidebar files per version +│ ├── version-4.x-sidebars.json +│ ├── version-3.x-sidebars.json +│ └── version-2.1-sidebars.json +├── sidebarsCommunity.json # Sidebar for community docs +├── versions.json # Active doc versions +├── docusaurus.config.js # Docusaurus configuration +└── local_dev.sh # Local development helper script ``` -Whether add new docs to existing directory or new directory, you need to update the relative path of the added docs in `sidebar.json`. - -```JSON -{ - "docs": [ - { - "type": "category", - "label": "Getting Started", - "items": [ - "get-starting/quick-start", - "get-starting/what-is-apache-doris" - ] - }, - { - "type": "category", - "label": "Install and Deploy", - "items": [ - "install/standard-deployment", - { - "type": "category", - "label": "Docker Deployment", - "items": [ - "install/construct-docker/build-docker-image", - "install/construct-docker/run-docker-cluster" - ] - } - ...... - } - ] - } -``` +## Contributing Documentation -**Updating Version 2.1/2.0/1.2** +For general contribution guidelines, see: -**1. Update content** +- [How to Contribute](https://doris.apache.org/community/how-to-contribute/contribute-to-doris) +- [How to Contribute Docs](https://doris.apache.org/community/how-to-contribute/contribute-doc) +- [Docs Format Specification](https://doris.apache.org/community/how-to-contribute/docs-format-specification) -- 2.1 version is modified in the `/versioned_docs/version-2.1` directory +### Editing Docs -- 2.0 version is modified in the `/versioned_docs / version-2.0`directory +When modifying docs, you typically need to update **both the English and Chinese versions** in the corresponding directories: -- 1.2 version is modified in the `/versioned_docs / version-1.2` directory +| Version | English | Chinese | Sidebar | +|---------|---------|---------|---------| +| Current (dev) | `docs/` | `i18n/zh-CN/.../current/` | `sidebars.ts` | +| 4.x | `versioned_docs/version-4.x/` | `i18n/zh-CN/.../version-4.x/` | `versioned_sidebars/version-4.x-sidebars.json` | +| 3.x | `versioned_docs/version-3.x/` | `i18n/zh-CN/.../version-3.x/` | `versioned_sidebars/version-3.x-sidebars.json` | +| 2.1 | `versioned_docs/version-2.1/` | `i18n/zh-CN/.../version-2.1/` | `versioned_sidebars/version-2.1-sidebars.json` | -```Plain -. -├── blog -├── community -├── docs -├── i18n -├── versioned_docs -│ ├── version-1.2 -│ ├── version-2.0 -│ ├── version-2.1 -``` +> **Note:** When adding a new page, you must also add its path to the corresponding sidebar file, otherwise it will not appear in the navigation. -**2. Update sidbar** +### Editing Blog Posts -The docs directory structure of the version docs is edited by `version-X.X-sidebar.json`. +Blog posts are located in the `blog/` directory. Submit a PR to add or modify blog content. -```Plain -. -├── blog -├── community -├── docs -├── i18n -├── versioned_docs -├── versioned_sidebars -│ ├── version-1.2-sidebars.json -│ └── version-2.0-sidebars.json -│ └── version-2.1-sidebars.json -``` +### Editing Community Docs -### 03 Community Docs Directory +Community docs are in `community/`, with navigation controlled by `sidebarsCommunity.json`. Chinese community content lives under `i18n/zh-CN/docusaurus-plugin-content-docs-community/`. -If you want to modify the community docs, please go to `community/` directory. +### Images -- For modifying the existing docs, please go to `community/` directory. +All images are stored in `static/images/`. Use hyphens to separate words in filenames (e.g., `query-profile-example.png`). -- For updating community docs directory, please modify the `sidebarsCommunity.json` to include appropriate relative path for the new document. - -```Markdown -. -├── blog -├── community -│ ├── design -│ │ ├── spark_load.md -│ │ ├── doris_storage_optimization.md -│ │ ├── grouping_sets_design.md -│ │ └── metadata-design.md -│ ├── ...... -│ ...... -├── sidebarsCommunity.json +```markdown +![Description of the image](/images/my-screenshot.png) ``` -### 04 Images Directory - -All images are located at `/static/images`. +## CI / Deployment -You can display images in simple syntax: ` ![Alt text for images description](co-locate file structure or link) ` +The site is deployed via GitHub Actions: -If the image file name consists of multiple English words, they should be separated by hyphens "-". +| Workflow | Trigger | Description | +|----------|---------|-------------| +| `cron-deploy-website.yml` | Daily at 01:00 AM | Syncs from Doris master branch and deploys | +| `manual-deploy-website.yml` | Manual | Deploy from a specified branch | +| `build-check.yml` | On PR | Validates the build passes (incrementally by detected version) | diff --git a/docusaurus.config.js b/docusaurus.config.js index b506729f29cd3..41c0da15cb0db 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -4,6 +4,14 @@ const VERSIONS = require('./versions.json'); const { markdownBoldPlugin } = require('./config/markdown-bold-plugin'); const { DEFAULT_VERSION } = require('./src/constant/version'); +// Allow filtering doc versions via environment variable. +// Usage: DOCS_VERSIONS="current,4.x" yarn docusaurus build +// This uses Docusaurus's native onlyIncludeVersions option +// instead of modifying versions.json. +const ONLY_VERSIONS = process.env.DOCS_VERSIONS + ? process.env.DOCS_VERSIONS.split(',').map(v => v.trim()).filter(Boolean) + : null; + const lightCodeTheme = themes.dracula; const logoImg = '/images/logo-doris.svg'; @@ -177,7 +185,12 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { - lastVersion: getLatestVersion(), + ...(ONLY_VERSIONS && { onlyIncludeVersions: ONLY_VERSIONS }), + // When filtering versions, lastVersion must be in the + // included list. Fall back to the first included version. + lastVersion: ONLY_VERSIONS && !ONLY_VERSIONS.includes(getLatestVersion()) + ? ONLY_VERSIONS[0] + : getLatestVersion(), versions: getDocsVersions(), sidebarPath: require.resolve('./sidebars.ts'), // editUrl: ({ locale, versionDocsDirPath, docPath }) => { diff --git a/local_dev.sh b/local_dev.sh new file mode 100755 index 0000000000000..4d2122030e237 --- /dev/null +++ b/local_dev.sh @@ -0,0 +1,359 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +############################################################## +# Local development script for doris-website +# +# Usage: +# ./local_dev.sh [command] [options] +# +# Commands: +# start Start dev server (English, default) +# start-zh Start dev server (Chinese) +# build Full production build (en + zh-CN) +# build-en Production build (English only) +# serve Serve a previous production build +# install Install dependencies only +# clean Clean build artifacts and caches +# help Show this help message +# +# Options: +# --port PORT Dev server port (default: 3000) +# --host HOST Dev server host (default: localhost) +# --skip-install Skip yarn install step +# --versions LIST Comma-separated versions to build +# e.g. --versions "4.x" (faster builds) +# --max-mem MB Node.js max old space size in MB +# (default: 8192) +# +# Examples: +# ./local_dev.sh # start English dev server +# ./local_dev.sh start --port 8080 # start on port 8080 +# ./local_dev.sh start-zh # start Chinese dev server +# ./local_dev.sh build-en # build English only +# ./local_dev.sh build # full build (slow) +# ./local_dev.sh build --versions "4.x" # build only 4.x version +# ./local_dev.sh clean # clean caches +############################################################## + +set -euo pipefail + +# ─── Resolve project root (directory of this script) ───────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="${SCRIPT_DIR}" + +# ─── Colors ────────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +info() { echo -e "${BLUE}[INFO]${NC} $*"; } +ok() { echo -e "${GREEN}[OK]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*"; } +step() { echo -e "\n${CYAN}${BOLD}==> $*${NC}"; } + +# ─── Discover Node.js and Yarn (no global env modification) ── +setup_node_env() { + # Try common Node.js install locations (macOS + Linux) + local search_paths=( + "/usr/local/bin" + "/usr/bin" + "/opt/homebrew/bin" + "${HOME}/.nvm/versions/node/$(ls -1 ${HOME}/.nvm/versions/node/ 2>/dev/null | sort -V | tail -1)/bin" + "${HOME}/.volta/bin" + "${HOME}/.fnm/aliases/default/bin" + "${HOME}/.local/bin" + "/snap/node/current/bin" + ) + + # If node is already on PATH, no need to modify + if command -v node &>/dev/null && command -v yarn &>/dev/null; then + NODE_BIN="$(command -v node)" + YARN_BIN="$(command -v yarn)" + return 0 + fi + + # Search for node in common locations + local found_path="" + for p in "${search_paths[@]}"; do + if [[ -x "${p}/node" ]]; then + found_path="${p}" + break + fi + done + + if [[ -z "${found_path}" ]]; then + error "Node.js not found! Please install Node.js >= 18." + error "Searched: ${search_paths[*]}" + echo "" + echo "Install options:" + echo " macOS: brew install node" + echo " Linux: curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs" + echo " or visit https://nodejs.org/" + exit 1 + fi + + # Prepend to PATH only within this script's process + export PATH="${found_path}:${PATH}" + NODE_BIN="${found_path}/node" + + # Ensure yarn is available; install locally if needed + if [[ -x "${found_path}/yarn" ]]; then + YARN_BIN="${found_path}/yarn" + elif command -v yarn &>/dev/null; then + YARN_BIN="$(command -v yarn)" + else + warn "Yarn not found, installing via npm (local to this script)..." + "${found_path}/npm" install -g yarn 2>/dev/null || { + error "Failed to install yarn. Please install manually: npm install -g yarn" + exit 1 + } + YARN_BIN="${found_path}/yarn" + fi +} + +# ─── Validate environment ─────────────────────────────────── +validate_env() { + step "Checking environment" + + setup_node_env + + local node_version + node_version="$("${NODE_BIN}" --version)" + local node_major="${node_version#v}" + node_major="${node_major%%.*}" + + if (( node_major < 18 )); then + error "Node.js >= 18 is required, found ${node_version}" + exit 1 + fi + + local yarn_version + yarn_version="$("${YARN_BIN}" --version)" + + ok "Node.js ${node_version} (${NODE_BIN})" + ok "Yarn v${yarn_version} (${YARN_BIN})" + ok "Project ${PROJECT_ROOT}" +} + +# ─── Install dependencies ─────────────────────────────────── +do_install() { + step "Installing dependencies" + cd "${PROJECT_ROOT}" + + if [[ -d "node_modules" ]] && [[ -f "node_modules/.yarn-integrity" ]]; then + info "node_modules exists, running yarn to sync..." + else + info "Installing from scratch (this may take a few minutes)..." + fi + + "${YARN_BIN}" install --frozen-lockfile 2>/dev/null || "${YARN_BIN}" install + ok "Dependencies installed" +} + +# ─── Set DOCS_VERSIONS env var for selective version builds ── +# This uses the onlyIncludeVersions option in docusaurus.config.js +# instead of modifying versions.json (which is fragile). +apply_versions_env() { + local filter="$1" + if [[ -n "${filter}" ]]; then + export DOCS_VERSIONS="${filter}" + info "DOCS_VERSIONS=${filter} (only these versions will be built)" + else + info "DOCS_VERSIONS not set (all versions will be built)" + fi +} + +# ─── Commands ──────────────────────────────────────────────── + +cmd_start() { + local port="${OPT_PORT}" + local host="${OPT_HOST}" + + validate_env + if [[ "${OPT_SKIP_INSTALL}" != "true" ]]; then + do_install + fi + + apply_versions_env "${OPT_VERSIONS}" + export NODE_OPTIONS="--max-old-space-size=${OPT_MAX_MEM}" + + step "Starting dev server (English) on ${host}:${port}" + info "Press Ctrl+C to stop" + echo "" + + cd "${PROJECT_ROOT}" + "${YARN_BIN}" docusaurus start --no-open --host "${host}" --port "${port}" +} + +cmd_start_zh() { + local port="${OPT_PORT}" + local host="${OPT_HOST}" + + validate_env + if [[ "${OPT_SKIP_INSTALL}" != "true" ]]; then + do_install + fi + + apply_versions_env "${OPT_VERSIONS}" + export NODE_OPTIONS="--max-old-space-size=${OPT_MAX_MEM}" + + step "Starting dev server (Chinese) on ${host}:${port}" + info "Press Ctrl+C to stop" + echo "" + + cd "${PROJECT_ROOT}" + "${YARN_BIN}" docusaurus start --no-open --locale zh-CN --host "${host}" --port "${port}" +} + +cmd_build() { + local locales="${1:-en}" + + validate_env + if [[ "${OPT_SKIP_INSTALL}" != "true" ]]; then + do_install + fi + + apply_versions_env "${OPT_VERSIONS}" + + step "Building site (locales: ${locales})" + info "NODE_OPTIONS=--max-old-space-size=${OPT_MAX_MEM}" + info "This may take 10-30 minutes for full builds..." + echo "" + + cd "${PROJECT_ROOT}" + export NODE_OPTIONS="--max-old-space-size=${OPT_MAX_MEM}" + + # Build locale arguments + local locale_args="" + for locale in ${locales}; do + locale_args+=" --locale ${locale}" + done + + "${YARN_BIN}" docusaurus build ${locale_args} + + ok "Build completed! Output in: ${PROJECT_ROOT}/build/" + info "Run './local_dev.sh serve' to preview the build." +} + +cmd_serve() { + validate_env + + if [[ ! -d "${PROJECT_ROOT}/build" ]]; then + error "No build directory found. Run './local_dev.sh build' first." + exit 1 + fi + + local port="${OPT_PORT}" + local host="${OPT_HOST}" + + step "Serving build on ${host}:${port}" + info "Press Ctrl+C to stop" + echo "" + + cd "${PROJECT_ROOT}" + "${YARN_BIN}" docusaurus serve --host "${host}" --port "${port}" +} + +cmd_clean() { + step "Cleaning build artifacts and caches" + + cd "${PROJECT_ROOT}" + + local dirs_to_clean=("build" ".docusaurus" "node_modules/.cache") + for d in "${dirs_to_clean[@]}"; do + if [[ -d "${d}" ]]; then + info "Removing ${d}/" + rm -rf "${d}" + fi + done + + ok "Clean completed" + info "Run './local_dev.sh install' to reinstall dependencies if needed." +} + +cmd_install() { + validate_env + do_install +} + +cmd_help() { + # Print the header comments of this script + sed -n '/^##*$/,/^##*$/p' "${BASH_SOURCE[0]}" | head -40 + echo "" + echo -e "${BOLD}Quick start:${NC}" + echo " ./local_dev.sh # Start English dev server" + echo " ./local_dev.sh start-zh # Start Chinese dev server" + echo " ./local_dev.sh build # Build English only (default)" + echo " ./local_dev.sh build-all # Full production build (en + zh-CN)" + echo "" +} + +# ─── Parse arguments ───────────────────────────────────────── + +COMMAND="${1:-start}" +shift 2>/dev/null || true + +OPT_PORT="3000" +OPT_HOST="localhost" +OPT_SKIP_INSTALL="false" +OPT_VERSIONS="current" +OPT_MAX_MEM="2048" + +while [[ $# -gt 0 ]]; do + case "$1" in + --port) + OPT_PORT="$2"; shift 2 ;; + --host) + OPT_HOST="$2"; shift 2 ;; + --skip-install) + OPT_SKIP_INSTALL="true"; shift ;; + --versions) + OPT_VERSIONS="$2"; shift 2 ;; + --max-mem) + OPT_MAX_MEM="$2"; shift 2 ;; + -h|--help) + COMMAND="help"; shift ;; + *) + error "Unknown option: $1" + cmd_help + exit 1 ;; + esac +done + +# ─── Dispatch command ──────────────────────────────────────── +case "${COMMAND}" in + start) cmd_start ;; + start-zh) cmd_start_zh ;; + build) cmd_build "en" ;; + build-all) cmd_build "en zh-CN" ;; + build-en) cmd_build "en" ;; + serve) cmd_serve ;; + install) cmd_install ;; + clean) cmd_clean ;; + help|-h|--help) cmd_help ;; + *) + error "Unknown command: ${COMMAND}" + cmd_help + exit 1 ;; +esac