From 5104d48346cb7171153a13be8e787cdb33612ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Mon, 3 Aug 2026 17:01:15 +0800 Subject: [PATCH] feat(docs): generate driver downloads from agent registries --- Makefile | 6 +-- docs/app/[lang]/drivers/DriversClient.tsx | 4 +- docs/lib/agentRegistry.test.ts | 22 ++++++++++- docs/lib/agentRegistry.ts | 45 +++++++++++++++++++---- 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index ffe8e2979..385e66368 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ export CONFIRM node_modules/.modules.yaml: package.json pnpm-lock.yaml $(PNPM) install --frozen-lockfile -docs/node_modules/.modules.yaml: docs/package.json docs/pnpm-lock.yaml - cd docs && $(PNPM) install --frozen-lockfile --ignore-workspace +docs/node_modules/.modules.yaml: docs/package.json docs/pnpm-lock.yaml docs/pnpm-workspace.yaml $(wildcard docs/patches/*.patch) + cd docs && $(PNPM) install --frozen-lockfile help: @printf '%s\n' 'DBX development targets:' @@ -59,7 +59,7 @@ install: $(PNPM) install --frozen-lockfile docs-install: - cd docs && $(PNPM) install --frozen-lockfile --ignore-workspace + cd docs && $(PNPM) install --frozen-lockfile check-tauri-dev-port: @if lsof -nP -iTCP:$(TAURI_DEV_PORT) -sTCP:LISTEN >/dev/null 2>&1; then \ diff --git a/docs/app/[lang]/drivers/DriversClient.tsx b/docs/app/[lang]/drivers/DriversClient.tsx index 13d2b17f0..8046bc525 100644 --- a/docs/app/[lang]/drivers/DriversClient.tsx +++ b/docs/app/[lang]/drivers/DriversClient.tsx @@ -19,7 +19,7 @@ const i18n = { drivers: "Database Drivers", driversDesc: "Single-driver .tar.zst packages for Java agents. Install the matching JRE separately when it is not already available.", nativeAgents: "Native Agents", - nativeAgentsDesc: "Platform-specific .tar.zst packages for DuckDB, Oracle, KingBase, and XuguDB. Import the package directly in Driver Manager.", + nativeAgentsDesc: "Platform-specific .tar.zst packages for DuckDB, Oracle, KingBase, XuguDB, and RabbitMQ. Import the package directly in Driver Manager.", jre: "Java Runtime (JRE)", jreDesc: "JRE packages used by Java agent-based database drivers such as SQL Server and Dameng.", download: "Download", @@ -55,7 +55,7 @@ const i18n = { drivers: "数据库驱动", driversDesc: "Java Agent 的单驱动 .tar.zst 包;目标机器尚未安装 JRE 时需要另外安装一次对应 JRE。", nativeAgents: "原生 Agent", - nativeAgentsDesc: "DuckDB、Oracle、人大金仓和虚谷的按平台 .tar.zst 单驱动包,可直接在驱动管理中导入。", + nativeAgentsDesc: "DuckDB、Oracle、人大金仓、虚谷和 RabbitMQ 的按平台 .tar.zst 单驱动包,可直接在驱动管理中导入。", jre: "Java 运行时 (JRE)", jreDesc: "Java Agent 驱动所需的 JRE 环境,例如 SQL Server、达梦等连接会使用。", download: "下载", diff --git a/docs/lib/agentRegistry.test.ts b/docs/lib/agentRegistry.test.ts index 8aacab4a6..101209325 100644 --- a/docs/lib/agentRegistry.test.ts +++ b/docs/lib/agentRegistry.test.ts @@ -31,7 +31,7 @@ test("non-release assets retain their official download link", () => { test("catalog falls back from R2 to CNB without using GitHub API", async () => { const requestedUrls: string[] = []; - const accessVersion = driverVersions.access; + const accessVersion = "9.9.9"; vi.stubGlobal( "fetch", vi.fn(async (input: string | URL | Request) => { @@ -70,6 +70,7 @@ test("catalog falls back from R2 to CNB without using GitHub API", async () => { assert.deepEqual(requestedUrls, ["https://dl.dbxio.com/agents/agent-registry.json", "https://cnb.cool/dbxio.com/dbx/-/releases/download/agents-latest/agent-registry.json"]); assert.equal(catalog?.drivers[0]?.key, "access"); + assert.equal(catalog?.drivers[0]?.version, accessVersion); assert.equal(catalog?.jres[0]?.platformKey, "macos-aarch64"); assert.equal(catalog?.bundles[0]?.platformKey, "macos-aarch64"); assert.equal(catalog?.bundles[0]?.url, "https://github.com/t8y2/dbx/releases/download/agents-v0.2.64/dbx-agents-offline-macos-aarch64.zip"); @@ -158,3 +159,22 @@ test("DuckDB native tar.zst packages appear in the native catalog", () => { }, ]); }); + +test("RabbitMQ native tar.zst packages appear in the native catalog", () => { + const entries = buildNativeAgentEntries([ + { + name: "dbx-agent-rabbitmq-0.1.1-windows-x64.tar.zst", + browser_download_url: "https://example.com/dbx-agent-rabbitmq-0.1.1-windows-x64.tar.zst", + size: 4096, + }, + ]); + + assert.deepEqual(entries.map(({ key, platformKey, filename }) => ({ key, platformKey, filename })), [ + { + key: "rabbitmq", + platformKey: "windows-x64", + filename: "dbx-agent-rabbitmq-0.1.1-windows-x64.tar.zst", + }, + ]); + assert.equal(entries[0]?.label, "RabbitMQ"); +}); diff --git a/docs/lib/agentRegistry.ts b/docs/lib/agentRegistry.ts index 9df7970b5..76191b796 100644 --- a/docs/lib/agentRegistry.ts +++ b/docs/lib/agentRegistry.ts @@ -26,6 +26,8 @@ interface AgentRegistryArtifact { interface AgentRegistryDriver { version?: string; + min_app_version?: string; + jre?: string; jar?: AgentRegistryArtifact; native?: Record; } @@ -89,7 +91,9 @@ const GITHUB_RELEASE_DOWNLOAD_PREFIX = "https://github.com/t8y2/dbx/releases/dow const CNB_RELEASE_DOWNLOAD_PREFIX = "https://cnb.cool/dbxio.com/dbx/-/releases/download/"; const MIN_APP_VERSION = "0.6.0"; const driverVersionMap = driverVersions as Record; -const nativeDriverKeys = new Set(["duckdb", "oracle", "kingbase", "xugu"]); +const nativeDriverKeys = ["duckdb", "oracle", "kingbase", "xugu", "rabbitmq"] as const; +const nativeDriverKeySet = new Set(nativeDriverKeys); +const nativeDriverKeyPattern = nativeDriverKeys.join("|"); const platformLabels: Record = { "macos-aarch64": "macOS (Apple Silicon)", @@ -127,6 +131,7 @@ const driverLabels: Record = { neo4j: "Neo4j", "oceanbase-oracle": "OceanBase Oracle Mode", oracle: "Oracle", + rabbitmq: "RabbitMQ", saphana: "SAP HANA", snowflake: "Snowflake", sundb: "SunDB", @@ -142,7 +147,7 @@ const driverLabels: Record = { }; const currentDriverKeys = Object.keys(driverVersionMap).sort((a, b) => labelForDriver(a).localeCompare(labelForDriver(b))); -const currentJavaDriverKeys = currentDriverKeys.filter((key) => !nativeDriverKeys.has(key)); +const currentJavaDriverKeys = currentDriverKeys.filter((key) => !nativeDriverKeySet.has(key)); const jreVersions: Record = { "21": "21", @@ -224,7 +229,14 @@ function assetMap(assets: GitHubReleaseAsset[]): Map } export function buildAgentDownloadCatalogFromRegistry(registry: AgentRegistry): AgentDownloadCatalog { - return buildAgentDownloadCatalog(registryReleaseAssets(registry)); + const assets = registryReleaseAssets(registry); + return { + jdbcPlugin: buildJdbcPluginDownloadEntry(), + bundles: buildOfflineBundleEntries(assets), + drivers: buildDriverEntriesFromRegistry(registry), + jres: buildJreEntries(assets), + nativeAgents: buildNativeAgentEntries(assets), + }; } export function buildAgentDownloadCatalog(assets: GitHubReleaseAsset[]): AgentDownloadCatalog { @@ -287,14 +299,33 @@ export function buildDriverEntries(assets: GitHubReleaseAsset[]): DriverDisplayE .filter((entry): entry is DriverDisplayEntry => entry !== null); } +function buildDriverEntriesFromRegistry(registry: AgentRegistry): DriverDisplayEntry[] { + return Object.entries(registry.drivers ?? {}) + .map(([key, driver]) => { + const jar = driver.jar; + if (nativeDriverKeySet.has(key) || !jar?.url || jar.size <= 0) return null; + + return { + key, + label: labelForDriver(key), + version: driver.version ?? driverVersionMap[key] ?? "", + minAppVersion: driver.min_app_version ?? MIN_APP_VERSION, + jar: { url: jar.url, size: jar.size }, + jre: driver.jre ?? "21", + }; + }) + .filter((entry): entry is DriverDisplayEntry => entry !== null) + .sort((a, b) => a.label.localeCompare(b.label)); +} + export function buildNativeAgentEntries(assets: GitHubReleaseAsset[]): NativeAgentDisplayEntry[] { const entries = new Map(); const platforms = "macos-aarch64|macos-x64|linux-aarch64|linux-x64|windows-aarch64|windows-x64"; for (const asset of assets) { - const packageMatch = new RegExp(`^dbx-agent-(duckdb|oracle|kingbase|xugu)-(.+)-(${platforms})\\.(?:tar\\.zst|zip)$`).exec(asset.name); - const versionedMatch = new RegExp(`^dbx-agent-(duckdb|oracle|kingbase|xugu)-(.+)-(${platforms})(?:\\.exe)?$`).exec(asset.name); - const legacyMatch = new RegExp(`^dbx-agent-(duckdb|oracle|kingbase|xugu)-(${platforms})(?:\\.exe)?$`).exec(asset.name); + const packageMatch = new RegExp(`^dbx-agent-(${nativeDriverKeyPattern})-(.+)-(${platforms})\\.(?:tar\\.zst|zip)$`).exec(asset.name); + const versionedMatch = new RegExp(`^dbx-agent-(${nativeDriverKeyPattern})-(.+)-(${platforms})(?:\\.exe)?$`).exec(asset.name); + const legacyMatch = new RegExp(`^dbx-agent-(${nativeDriverKeyPattern})-(${platforms})(?:\\.exe)?$`).exec(asset.name); const match = packageMatch ?? versionedMatch ?? legacyMatch; if (!match) continue; @@ -302,7 +333,7 @@ export function buildNativeAgentEntries(assets: GitHubReleaseAsset[]): NativeAge const key = match[1]; const version = legacyMatch ? (driverVersionMap[key] ?? "") : match[2]; const platformKey = legacyMatch ? match[2] : match[3]; - if (!nativeDriverKeys.has(key)) continue; + if (!nativeDriverKeySet.has(key)) continue; const entryKey = `${key}:${platformKey}`; const existing = entries.get(entryKey); if (existing?.packaged && !packaged) continue;