diff --git a/lib/db_schema.sh b/lib/db_schema.sh new file mode 100644 index 0000000..a655ae3 --- /dev/null +++ b/lib/db_schema.sh @@ -0,0 +1,237 @@ +#!/bin/bash + +# Copyright 1999-2025 Alibaba Group Holding Ltd. +# 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. + +# Database Schema Export Module +# Exports full schema SQL for a given Nacos version and database type + +# Supported database types +DB_SCHEMA_SUPPORTED_TYPES=("mysql" "postgresql") + +# Cache directory for downloaded schema files +DB_SCHEMA_CACHE_DIR="${NACOS_CACHE_DIR:-$HOME/.nacos/cache}" + +# ============================================================================ +# Schema File Name Mapping +# ============================================================================ + +# Map db_type to the actual schema file name used in Nacos repository. +# PostgreSQL uses "pg-schema.sql" not "postgresql-schema.sql". +_schema_filename() { + local db_type="$1" + case "$db_type" in + postgresql) echo "pg-schema.sql" ;; + *) echo "${db_type}-schema.sql" ;; + esac +} + +# ============================================================================ +# Validation +# ============================================================================ + +validate_db_type() { + local db_type="$1" + if [ -z "$db_type" ]; then + print_error "Database type is required" >&2 + return 1 + fi + for supported in "${DB_SCHEMA_SUPPORTED_TYPES[@]}"; do + if [ "$db_type" = "$supported" ]; then + return 0 + fi + done + print_error "Unsupported database type: $db_type" >&2 + print_info "Supported types: ${DB_SCHEMA_SUPPORTED_TYPES[*]}" >&2 + return 1 +} + +# ============================================================================ +# Local Schema Lookup +# ============================================================================ + +# Find schema file from a local Nacos installation. +# Uses NACOS_INSTALL_BASE env var if set (for testing), otherwise $HOME/.nacos/nacos-server-$VERSION. +# Outputs the file path to stdout if found, empty otherwise. +find_local_schema() { + local version="$1" + local db_type="$2" + local nacos_home="${NACOS_INSTALL_BASE:-$HOME/.nacos/nacos-server-$version}/nacos" + local filename + filename=$(_schema_filename "$db_type") + + # New-style: plugin-ext directory (Nacos >3.1.1) + local new_path="$nacos_home/plugin-ext/nacos-datasource-plugin-${db_type}/${filename}" + if [ -f "$new_path" ]; then + echo "$new_path" + return 0 + fi + + # Old-style: conf directory (Nacos <=3.1.1) + local old_path="$nacos_home/conf/${filename}" + if [ -f "$old_path" ]; then + echo "$old_path" + return 0 + fi + + return 1 +} + +# ============================================================================ +# Remote Schema Download +# ============================================================================ + +# Returns the cache file path for a given version and type. +_schema_cache_path() { + local version="$1" + local db_type="$2" + echo "${DB_SCHEMA_CACHE_DIR}/${version}-${db_type}-schema.sql" +} + +# Build the GitHub raw URL for a schema file. +# Tries new plugin path first, falls back to old distribution/conf path. +_schema_github_urls() { + local version="$1" + local db_type="$2" + local filename + filename=$(_schema_filename "$db_type") + # New path (Nacos >=3.2.0, after plugin refactor) + echo "https://raw.githubusercontent.com/alibaba/nacos/${version}/plugin-default-impl/nacos-default-datasource-plugin/nacos-datasource-plugin-${db_type}/src/main/resources/META-INF/${filename}" + # Old path (Nacos <3.2.0) + echo "https://raw.githubusercontent.com/alibaba/nacos/${version}/distribution/conf/${filename}" +} + +# Download schema from GitHub with new-path-first fallback. +# Caches the result. Outputs the local file path on success. +download_schema() { + local version="$1" + local db_type="$2" + + # Check cache first + local cache_file + cache_file=$(_schema_cache_path "$version" "$db_type") + if [ -f "$cache_file" ] && [ -s "$cache_file" ]; then + print_info "Using cached schema: $cache_file" >&2 + echo "$cache_file" + return 0 + fi + + mkdir -p "$DB_SCHEMA_CACHE_DIR" 2>/dev/null + + # Try each URL in order + local urls + urls=$(_schema_github_urls "$version" "$db_type") + while IFS= read -r url; do + print_info "Downloading schema from: $url" >&2 + if curl -sSL --fail "$url" -o "$cache_file" 2>/dev/null; then + if [ -s "$cache_file" ]; then + print_info "Schema cached to: $cache_file" >&2 + echo "$cache_file" + return 0 + fi + fi + rm -f "$cache_file" + done <<< "$urls" + + print_error "Failed to download schema for Nacos $version ($db_type)" >&2 + print_info "Check that version tag '$version' exists at https://github.com/alibaba/nacos" >&2 + return 1 +} + +# ============================================================================ +# Interactive Prompts +# ============================================================================ + +# Prompt user to select database type interactively. +# Only works when stdin is a terminal. Outputs selected type to stdout. +_prompt_db_type() { + if [ ! -t 0 ]; then + print_error "Database type (--type) is required in non-interactive mode" >&2 + return 1 + fi + echo "Select database type:" >&2 + echo " 1. MySQL" >&2 + echo " 2. PostgreSQL" >&2 + read -p "Enter choice (1/2): " -r choice + case "$choice" in + 1) echo "mysql" ;; + 2) echo "postgresql" ;; + *) + print_error "Invalid choice: $choice" >&2 + return 1 + ;; + esac +} + +# Prompt user to enter version interactively. +_prompt_version() { + if [ ! -t 0 ]; then + print_error "Version (--version/-v) is required in non-interactive mode" >&2 + return 1 + fi + read -p "Enter Nacos version: " -r version + if [ -z "$version" ]; then + print_error "Version cannot be empty" >&2 + return 1 + fi + echo "$version" +} + +# ============================================================================ +# Main Entry Point +# ============================================================================ + +# Main function called from nacos-setup.sh. +# Args: version (optional), db_type (optional) +# Outputs schema SQL to stdout, all logs to stderr. +db_schema_main() { + local version="$1" + local db_type="$2" + + # Resolve missing arguments via interactive prompts + if [ -z "$db_type" ]; then + db_type=$(_prompt_db_type) || return 1 + fi + + if [ -z "$version" ]; then + # Try to detect locally installed version + local installed_dir="$HOME/.nacos" + local detected_version="" + if [ -d "$installed_dir" ]; then + detected_version=$(ls -d "$installed_dir"/nacos-server-*/ 2>/dev/null | sort -V | tail -1 | sed 's|.*/nacos-server-||;s|/||') + fi + if [ -n "$detected_version" ]; then + print_info "Detected installed version: $detected_version" >&2 + version="$detected_version" + else + version=$(_prompt_version) || return 1 + fi + fi + + # Validate + validate_db_type "$db_type" || return 1 + + print_info "Exporting $db_type schema for Nacos $version..." >&2 + + # Try local first + local schema_file + schema_file=$(find_local_schema "$version" "$db_type" 2>/dev/null) || true + + # Fallback to download + if [ -z "$schema_file" ]; then + schema_file=$(download_schema "$version" "$db_type") || return 1 + fi + + # Output SQL to stdout + cat "$schema_file" +} diff --git a/nacos-setup.sh b/nacos-setup.sh index 3b2eb50..a77acc5 100755 --- a/nacos-setup.sh +++ b/nacos-setup.sh @@ -146,6 +146,10 @@ DB_CONF_MODE="" DB_CONF_FILE="" DB_CONF_ACTION="" +# Database schema export mode +DB_SCHEMA_MODE=false +DB_SCHEMA_TYPE="" + # ============================================================================ # Usage Information # ============================================================================ @@ -174,6 +178,7 @@ COMMON OPTIONS: -db-conf [NAME] Use external datasource (default: default) db-conf edit [NAME] Edit datasource configuration db-conf show [NAME] Show datasource configuration + db-schema [-v VERSION] [--type TYPE] Export database schema SQL to stdout -h, --help Show this help message STANDALONE MODE OPTIONS: @@ -224,6 +229,16 @@ EXAMPLES: # Configure global database settings bash nacos-setup.sh --datasource-conf + Database Schema: + # Export MySQL schema for version 3.2.0 + bash nacos-setup.sh db-schema -v 3.2.0 --type mysql + + # Export and pipe directly to database + bash nacos-setup.sh db-schema -v 3.2.0 --type mysql | mysql -h localhost -u root -p nacos + + # Interactive mode (prompts for version and type) + bash nacos-setup.sh db-schema + VERSION REQUIREMENTS: - Minimum supported: Nacos 2.4.0 - Nacos 3.x requires Java 17+ @@ -286,6 +301,36 @@ parse_arguments() { exit 1 fi ;; + db-schema) + DB_SCHEMA_MODE=true + shift + # Parse db-schema specific options + while [[ $# -gt 0 ]]; do + case $1 in + -v|--version) + if [ -z "$2" ] || [[ "$2" == -* ]]; then + print_error "db-schema: -v/--version requires a version number" + exit 1 + fi + VERSION="$2" + USER_SPECIFIED_VERSION=true + shift 2 + ;; + -t|--type) + if [ -z "$2" ] || [[ "$2" == -* ]]; then + print_error "db-schema: --type requires a database type (mysql/postgresql)" + exit 1 + fi + DB_SCHEMA_TYPE="$2" + shift 2 + ;; + *) + print_error "Unknown db-schema option: $1" + exit 1 + ;; + esac + done + ;; --adv) ADVANCED_MODE=true shift @@ -489,6 +534,17 @@ main() { esac fi + # Handle db-schema mode (local mode, doesn't need version fetching or Java) + if [ "$DB_SCHEMA_MODE" = true ]; then + source "$LIB_DIR/db_schema.sh" + local schema_version="" + if [ "$USER_SPECIFIED_VERSION" = true ]; then + schema_version="$VERSION" + fi + db_schema_main "$schema_version" "$DB_SCHEMA_TYPE" + exit $? + fi + # Initialize version (fetch from remote only if user didn't specify -v) init_version echo "" diff --git a/tests/run_all.sh b/tests/run_all.sh index 9afd97d..3c1472c 100755 --- a/tests/run_all.sh +++ b/tests/run_all.sh @@ -26,6 +26,7 @@ test_cases=( "test_java.sh:Java Environment" "test_download.sh:Download & Cache" "test_dbconf.sh:--db-conf Feature" + "test_db_schema.sh:db-schema Feature" "test_package.sh:Package Script" "test_bugs.sh:Bug Verification" "test_install_paths.sh:Install Path Constants" diff --git a/tests/test_db_schema.sh b/tests/test_db_schema.sh new file mode 100644 index 0000000..9030477 --- /dev/null +++ b/tests/test_db_schema.sh @@ -0,0 +1,221 @@ +#!/bin/bash +# +# db-schema Feature Tests - 数据库 Schema 导出功能测试 + +source "$(dirname "$0")/common.sh" + +echo "=== Test Group: db-schema Feature ===" + +# Load db_schema module +if [ -f "$LIB_DIR/db_schema.sh" ]; then + source "$LIB_DIR/common.sh" 2>/dev/null + source "$LIB_DIR/db_schema.sh" 2>/dev/null + + # --- validate_db_type tests --- + test_info "Testing validate_db_type function" + + if validate_db_type "mysql" 2>/dev/null; then + test_pass "validate_db_type accepts 'mysql'" + else + test_fail "validate_db_type should accept 'mysql'" + fi + + if validate_db_type "postgresql" 2>/dev/null; then + test_pass "validate_db_type accepts 'postgresql'" + else + test_fail "validate_db_type should accept 'postgresql'" + fi + + if validate_db_type "oracle" 2>/dev/null; then + test_fail "validate_db_type should reject 'oracle'" + else + test_pass "validate_db_type rejects 'oracle'" + fi + + if validate_db_type "" 2>/dev/null; then + test_fail "validate_db_type should reject empty string" + else + test_pass "validate_db_type rejects empty string" + fi + + # --- _schema_filename tests --- + echo "" + test_info "Testing _schema_filename mapping" + + result=$(_schema_filename "mysql") + if [ "$result" = "mysql-schema.sql" ]; then + test_pass "_schema_filename maps mysql -> mysql-schema.sql" + else + test_fail "_schema_filename mysql: expected mysql-schema.sql, got '$result'" + fi + + result=$(_schema_filename "postgresql") + if [ "$result" = "pg-schema.sql" ]; then + test_pass "_schema_filename maps postgresql -> pg-schema.sql" + else + test_fail "_schema_filename postgresql: expected pg-schema.sql, got '$result'" + fi + + # --- find_local_schema tests --- + echo "" + test_info "Testing find_local_schema function" + + # Create fake Nacos install with old-style schema + OLD_NACOS_HOME="/tmp/test_nacos_old/nacos" + mkdir -p "$OLD_NACOS_HOME/conf" + echo "-- old mysql schema" > "$OLD_NACOS_HOME/conf/mysql-schema.sql" + + result=$(NACOS_INSTALL_BASE="/tmp/test_nacos_old" find_local_schema "test" "mysql" 2>/dev/null) + if [ "$result" = "$OLD_NACOS_HOME/conf/mysql-schema.sql" ]; then + test_pass "find_local_schema finds old-style schema (conf/)" + else + test_fail "find_local_schema old-style: expected $OLD_NACOS_HOME/conf/mysql-schema.sql, got '$result'" + fi + + # Create fake Nacos install with new-style schema (plugin-ext) + NEW_NACOS_HOME="/tmp/test_nacos_new/nacos" + mkdir -p "$NEW_NACOS_HOME/plugin-ext/nacos-datasource-plugin-mysql" + echo "-- new mysql schema" > "$NEW_NACOS_HOME/plugin-ext/nacos-datasource-plugin-mysql/mysql-schema.sql" + + result=$(NACOS_INSTALL_BASE="/tmp/test_nacos_new" find_local_schema "test" "mysql" 2>/dev/null) + if [ "$result" = "$NEW_NACOS_HOME/plugin-ext/nacos-datasource-plugin-mysql/mysql-schema.sql" ]; then + test_pass "find_local_schema finds new-style schema (plugin-ext/)" + else + test_fail "find_local_schema new-style: expected plugin-ext path, got '$result'" + fi + + # New-style takes priority when both exist + BOTH_NACOS_HOME="/tmp/test_nacos_both/nacos" + mkdir -p "$BOTH_NACOS_HOME/conf" + mkdir -p "$BOTH_NACOS_HOME/plugin-ext/nacos-datasource-plugin-mysql" + echo "-- old" > "$BOTH_NACOS_HOME/conf/mysql-schema.sql" + echo "-- new" > "$BOTH_NACOS_HOME/plugin-ext/nacos-datasource-plugin-mysql/mysql-schema.sql" + + result=$(NACOS_INSTALL_BASE="/tmp/test_nacos_both" find_local_schema "test" "mysql" 2>/dev/null) + if [ "$result" = "$BOTH_NACOS_HOME/plugin-ext/nacos-datasource-plugin-mysql/mysql-schema.sql" ]; then + test_pass "find_local_schema prefers new-style when both exist" + else + test_fail "find_local_schema priority: expected new-style path, got '$result'" + fi + + # PostgreSQL uses pg-schema.sql filename + PG_NACOS_HOME="/tmp/test_nacos_pg/nacos" + mkdir -p "$PG_NACOS_HOME/conf" + echo "-- pg schema" > "$PG_NACOS_HOME/conf/pg-schema.sql" + + result=$(NACOS_INSTALL_BASE="/tmp/test_nacos_pg" find_local_schema "test" "postgresql" 2>/dev/null) + if [ "$result" = "$PG_NACOS_HOME/conf/pg-schema.sql" ]; then + test_pass "find_local_schema finds postgresql as pg-schema.sql" + else + test_fail "find_local_schema postgresql: expected pg-schema.sql path, got '$result'" + fi + rm -rf /tmp/test_nacos_pg + + # Non-existent version returns empty + result=$(NACOS_INSTALL_BASE="/tmp/test_nacos_nonexistent" find_local_schema "99.99.99" "mysql" 2>/dev/null) + if [ -z "$result" ]; then + test_pass "find_local_schema returns empty for non-existent version" + else + test_fail "find_local_schema non-existent: expected empty, got '$result'" + fi + + # Cleanup + rm -rf /tmp/test_nacos_old /tmp/test_nacos_new /tmp/test_nacos_both + + # --- download_schema / cache tests --- + echo "" + test_info "Testing download_schema cache logic" + + # Test cache hit: pre-populate cache and verify it's used + TEST_CACHE_DIR="/tmp/test_db_schema_cache" + mkdir -p "$TEST_CACHE_DIR" + echo "-- cached mysql schema" > "$TEST_CACHE_DIR/3.2.0-mysql-schema.sql" + + result=$(DB_SCHEMA_CACHE_DIR="$TEST_CACHE_DIR" download_schema "3.2.0" "mysql" 2>/dev/null) + if [ "$result" = "$TEST_CACHE_DIR/3.2.0-mysql-schema.sql" ]; then + test_pass "download_schema returns cached file when present" + else + test_fail "download_schema cache hit: expected cache path, got '$result'" + fi + + # Test cache file naming convention + expected_cache_name="3.2.0-BETA-postgresql-schema.sql" + result=$(DB_SCHEMA_CACHE_DIR="$TEST_CACHE_DIR" _schema_cache_path "3.2.0-BETA" "postgresql") + if [ "$(basename "$result")" = "$expected_cache_name" ]; then + test_pass "Cache file naming: $expected_cache_name" + else + test_fail "Cache file naming: expected $expected_cache_name, got $(basename "$result")" + fi + + rm -rf "$TEST_CACHE_DIR" + + # --- db_schema_main integration tests --- + echo "" + test_info "Testing db_schema_main function" + + # Test: stderr/stdout separation — log messages should NOT appear in stdout + TEST_CACHE_DIR2="/tmp/test_db_schema_main" + mkdir -p "$TEST_CACHE_DIR2" + echo "CREATE TABLE test_table (id INT);" > "$TEST_CACHE_DIR2/3.2.0-mysql-schema.sql" + + stdout_output=$(DB_SCHEMA_CACHE_DIR="$TEST_CACHE_DIR2" NACOS_INSTALL_BASE="/tmp/nonexistent" db_schema_main "3.2.0" "mysql" 2>/dev/null) + if echo "$stdout_output" | grep -q "CREATE TABLE"; then + test_pass "db_schema_main outputs SQL to stdout" + else + test_fail "db_schema_main should output SQL to stdout, got: '$stdout_output'" + fi + + if echo "$stdout_output" | grep -q "\[INFO\]"; then + test_fail "db_schema_main should not leak log messages to stdout" + else + test_pass "db_schema_main keeps log messages on stderr only" + fi + + # Test: invalid type reports error + stderr_output=$(db_schema_main "3.2.0" "oracle" 2>&1 >/dev/null) + if echo "$stderr_output" | grep -qi "unsupported"; then + test_pass "db_schema_main reports error for invalid type" + else + test_fail "db_schema_main should report unsupported type error" + fi + + rm -rf "$TEST_CACHE_DIR2" + + # --- nacos-setup.sh integration tests --- + echo "" + test_info "Testing nacos-setup.sh db-schema integration" + + if [ -f "$TEST_DIR/nacos-setup.sh" ]; then + # db-schema should not fetch versions (local mode, like db-conf) + output=$(bash "$TEST_DIR/nacos-setup.sh" db-schema --type mysql -v 99.99.99 2>&1) + if echo "$output" | grep -q "Fetching versions"; then + test_fail "db-schema should not fetch versions" + else + test_pass "db-schema does not fetch versions (local mode)" + fi + + # db-schema with unknown type should fail + output=$(bash "$TEST_DIR/nacos-setup.sh" db-schema --type oracle -v 3.2.0 2>&1) + exit_code=$? + if [ $exit_code -ne 0 ]; then + test_pass "db-schema exits non-zero for unsupported type" + else + test_fail "db-schema should exit non-zero for unsupported type" + fi + + # db-schema shows in help output + output=$(bash "$TEST_DIR/nacos-setup.sh" -h 2>&1) + if echo "$output" | grep -q "db-schema"; then + test_pass "db-schema appears in help output" + else + test_fail "db-schema missing from help output" + fi + else + test_fail "nacos-setup.sh not found" + fi +else + test_fail "db_schema.sh not found" +fi + +echo "" +test_summary diff --git a/windows/lib/db_schema.ps1 b/windows/lib/db_schema.ps1 new file mode 100644 index 0000000..52e18b4 --- /dev/null +++ b/windows/lib/db_schema.ps1 @@ -0,0 +1,213 @@ +# Database Schema Export Module (Windows PowerShell) +# Exports full schema SQL for a given Nacos version and database type + +# Supported database types +$Script:SupportedDbTypes = @("mysql", "postgresql") + +# Cache directory for downloaded schema files +$Script:DbSchemaCacheDir = if ($env:NACOS_CACHE_DIR) { + $env:NACOS_CACHE_DIR +} else { + $userHome = if ($realUserProfile) { $realUserProfile } elseif ($env:USERPROFILE) { $env:USERPROFILE } elseif ($env:HOME) { $env:HOME } else { "." } + Join-Path $userHome ".nacos\cache" +} + +# ============================================================================ +# Schema File Name Mapping +# ============================================================================ + +function Get-SchemaFilename([string]$DbType) { + switch ($DbType) { + "postgresql" { return "pg-schema.sql" } + default { return "${DbType}-schema.sql" } + } +} + +# ============================================================================ +# Validation +# ============================================================================ + +function Test-DbType([string]$DbType) { + if (-not $DbType) { + Write-ErrorMsg "Database type is required" + return $false + } + if ($DbType -in $Script:SupportedDbTypes) { + return $true + } + Write-ErrorMsg "Unsupported database type: $DbType" + Write-Info "Supported types: $($Script:SupportedDbTypes -join ', ')" + return $false +} + +# ============================================================================ +# Local Schema Lookup +# ============================================================================ + +function Find-LocalSchema([string]$Version, [string]$DbType) { + $userHome = if ($realUserProfile) { $realUserProfile } elseif ($env:USERPROFILE) { $env:USERPROFILE } elseif ($env:HOME) { $env:HOME } else { "." } + $installBase = if ($env:NACOS_INSTALL_BASE) { $env:NACOS_INSTALL_BASE } else { Join-Path $userHome ".nacos\nacos-server-$Version" } + $nacosHome = Join-Path $installBase "nacos" + $filename = Get-SchemaFilename $DbType + + # New-style: plugin-ext directory (Nacos >3.1.1) + $newPath = Join-Path $nacosHome "plugin-ext\nacos-datasource-plugin-${DbType}\${filename}" + if (Test-Path $newPath) { + return $newPath + } + + # Old-style: conf directory (Nacos <=3.1.1) + $oldPath = Join-Path $nacosHome "conf\${filename}" + if (Test-Path $oldPath) { + return $oldPath + } + + return $null +} + +# ============================================================================ +# Remote Schema Download +# ============================================================================ + +function Get-SchemaCachePath([string]$Version, [string]$DbType) { + return Join-Path $Script:DbSchemaCacheDir "${Version}-${DbType}-schema.sql" +} + +function Get-SchemaGithubUrls([string]$Version, [string]$DbType) { + $filename = Get-SchemaFilename $DbType + return @( + # New path (Nacos >=3.2.0, after plugin refactor) + "https://raw.githubusercontent.com/alibaba/nacos/${Version}/plugin-default-impl/nacos-default-datasource-plugin/nacos-datasource-plugin-${DbType}/src/main/resources/META-INF/${filename}" + # Old path (Nacos <3.2.0) + "https://raw.githubusercontent.com/alibaba/nacos/${Version}/distribution/conf/${filename}" + ) +} + +function Download-Schema([string]$Version, [string]$DbType) { + $cacheFile = Get-SchemaCachePath $Version $DbType + + # Check cache first + if ((Test-Path $cacheFile) -and (Get-Item $cacheFile).Length -gt 0) { + Write-Info "Using cached schema: $cacheFile" + return $cacheFile + } + + # Ensure cache directory exists + $cacheDir = Split-Path $cacheFile -Parent + if (-not (Test-Path $cacheDir)) { + New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null + } + + # Try each URL in order + $urls = Get-SchemaGithubUrls $Version $DbType + foreach ($url in $urls) { + Write-Info "Downloading schema from: $url" + try { + Invoke-WebRequest -Uri $url -OutFile $cacheFile -UseBasicParsing -ErrorAction Stop + if ((Test-Path $cacheFile) -and (Get-Item $cacheFile).Length -gt 0) { + # Verify it's not a 404 page + $content = Get-Content $cacheFile -First 1 -ErrorAction SilentlyContinue + if ($content -and $content -notmatch "^404:") { + Write-Info "Schema cached to: $cacheFile" + return $cacheFile + } + } + } catch { + # Download failed, try next URL + } + if (Test-Path $cacheFile) { Remove-Item $cacheFile -Force -ErrorAction SilentlyContinue } + } + + Write-ErrorMsg "Failed to download schema for Nacos $Version ($DbType)" + Write-Info "Check that version tag '$Version' exists at https://github.com/alibaba/nacos" + return $null +} + +# ============================================================================ +# Interactive Prompts +# ============================================================================ + +function Read-DbTypePrompt { + # Check if running in non-interactive mode + if (-not [Environment]::UserInteractive) { + Write-ErrorMsg "Database type (--type) is required in non-interactive mode" + return $null + } + Write-Host "Select database type:" + Write-Host " 1. MySQL" + Write-Host " 2. PostgreSQL" + $choice = Read-Host "Enter choice (1/2)" + switch ($choice) { + "1" { return "mysql" } + "2" { return "postgresql" } + default { + Write-ErrorMsg "Invalid choice: $choice" + return $null + } + } +} + +function Read-VersionPrompt { + if (-not [Environment]::UserInteractive) { + Write-ErrorMsg "Version (--version/-v) is required in non-interactive mode" + return $null + } + $version = Read-Host "Enter Nacos version" + if (-not $version) { + Write-ErrorMsg "Version cannot be empty" + return $null + } + return $version +} + +# ============================================================================ +# Main Entry Point +# ============================================================================ + +function Export-DbSchema([string]$Version, [string]$DbType) { + # Resolve missing arguments via interactive prompts + if (-not $DbType) { + $DbType = Read-DbTypePrompt + if (-not $DbType) { return $false } + } + + if (-not $Version) { + # Try to detect locally installed version + $userHome = if ($realUserProfile) { $realUserProfile } elseif ($env:USERPROFILE) { $env:USERPROFILE } elseif ($env:HOME) { $env:HOME } else { "." } + $installDir = Join-Path $userHome ".nacos" + $detectedVersion = $null + if (Test-Path $installDir) { + $serverDirs = Get-ChildItem -Path $installDir -Directory -Filter "nacos-server-*" -ErrorAction SilentlyContinue | + Sort-Object Name | + Select-Object -Last 1 + if ($serverDirs) { + $detectedVersion = $serverDirs.Name -replace '^nacos-server-', '' + } + } + if ($detectedVersion) { + Write-Info "Detected installed version: $detectedVersion" + $Version = $detectedVersion + } else { + $Version = Read-VersionPrompt + if (-not $Version) { return $false } + } + } + + # Validate + if (-not (Test-DbType $DbType)) { return $false } + + Write-Info "Exporting $DbType schema for Nacos $Version..." + + # Try local first + $schemaFile = Find-LocalSchema $Version $DbType + + # Fallback to download + if (-not $schemaFile) { + $schemaFile = Download-Schema $Version $DbType + if (-not $schemaFile) { return $false } + } + + # Output SQL to stdout + Get-Content $schemaFile -Raw + return $true +} diff --git a/windows/nacos-setup.ps1 b/windows/nacos-setup.ps1 index 18ba1f5..8f7e3b6 100644 --- a/windows/nacos-setup.ps1 +++ b/windows/nacos-setup.ps1 @@ -208,6 +208,8 @@ $Global:JoinMode = $JoinMode $Global:LeaveMode = $LeaveMode $Global:NodeIndex = $NodeIndex $Global:DatasourceConfMode = $DatasourceConfMode +$Global:DbSchemaMode = $false +$Global:DbSchemaType = "" function Global:Invoke-NacosSetupCleanup { if ($Global:CleanupDone) { return } @@ -246,6 +248,7 @@ function Print-Usage { Write-Host " -db-conf [NAME] Use external datasource (default: default)" Write-Host " db-conf edit [NAME] Edit datasource configuration" Write-Host " db-conf show [NAME] Show datasource configuration" + Write-Host " db-schema [-v VER] [--type TYPE] Export database schema SQL to stdout" Write-Host " -h, --help Show this help message" Write-Host "" Write-Host "Cluster Options:" @@ -313,6 +316,42 @@ function Parse-Arguments($argv) { exit 1 } } + "db-schema" { + $Global:DbSchemaMode = $true + $i++ + # Parse db-schema specific options + while ($i -lt $argv.Count) { + switch ($argv[$i]) { + "-v" { + if ($i + 1 -ge $argv.Count -or $argv[$i + 1] -match "^-") { + Write-ErrorMsg "db-schema: -v/--version requires a version number"; exit 1 + } + $Global:Version = $argv[$i + 1]; $script:UserSpecifiedVersion = $true; $i += 2 + } + "--version" { + if ($i + 1 -ge $argv.Count -or $argv[$i + 1] -match "^-") { + Write-ErrorMsg "db-schema: -v/--version requires a version number"; exit 1 + } + $Global:Version = $argv[$i + 1]; $script:UserSpecifiedVersion = $true; $i += 2 + } + "-t" { + if ($i + 1 -ge $argv.Count -or $argv[$i + 1] -match "^-") { + Write-ErrorMsg "db-schema: --type requires a database type (mysql/postgresql)"; exit 1 + } + $Global:DbSchemaType = $argv[$i + 1]; $i += 2 + } + "--type" { + if ($i + 1 -ge $argv.Count -or $argv[$i + 1] -match "^-") { + Write-ErrorMsg "db-schema: --type requires a database type (mysql/postgresql)"; exit 1 + } + $Global:DbSchemaType = $argv[$i + 1]; $i += 2 + } + default { + Write-ErrorMsg "Unknown db-schema option: $($argv[$i])"; exit 1 + } + } + } + } "--adv" { $Global:AdvancedMode = $true; $i++ } "--daemon" { $Global:DaemonMode = $true; $i++ } "--clean" { $Global:CleanMode = $true; $i++ } @@ -712,6 +751,15 @@ try { } } + # Handle db-schema mode (local mode, doesn't need version fetching or Java) + if ($Global:DbSchemaMode) { + . (Join-Path $PSScriptRoot "lib\db_schema.ps1") + $schemaVersion = if ($script:UserSpecifiedVersion) { $Global:Version } else { "" } + $result = Export-DbSchema $schemaVersion $Global:DbSchemaType + if ($result -eq $false) { exit 1 } + exit 0 + } + # Initialize version (fetch from remote only if user didn't specify -v) Initialize-Version Write-Host ""