From 824aafbdeab7d8ece6ff3af71db0fc471c094d27 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Wed, 29 Apr 2026 17:20:47 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20chore(driver):=20=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=94=9F=E6=88=90=E9=A9=B1=E5=8A=A8=E4=BB=A3=E7=90=86?= =?UTF-8?q?=20revision?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增脚本按 optional driver-agent 源码依赖生成 revision 指纹 - 构建脚本与 dev/release workflow 在打包前自动刷新 revision - 生成驱动 revision 映射并补充 optional driver 覆盖校验 --- .github/workflows/dev-build.yml | 1 + .github/workflows/release.yml | 1 + build-driver-agents.sh | 2 + build-release.sh | 13 ++ internal/db/driver_agent_revisions_gen.go | 21 ++ internal/db/driver_support.go | 9 + internal/db/driver_support_test.go | 11 + tools/generate-driver-agent-revisions.sh | 251 ++++++++++++++++++++++ 8 files changed, 309 insertions(+) create mode 100644 internal/db/driver_agent_revisions_gen.go create mode 100755 tools/generate-driver-agent-revisions.sh diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index f5601e6..3e58839 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -246,6 +246,7 @@ jobs: run: | set -euo pipefail DEV_VERSION="${{ steps.version.outputs.version }}" + ./tools/generate-driver-agent-revisions.sh --platform "${{ matrix.platform }}" if [ -n "${{ matrix.wails_tags }}" ]; then wails build -platform ${{ matrix.platform }} -clean -o ${{ matrix.build_name }} -tags "${{ matrix.wails_tags }}" -ldflags "-s -w -X GoNavi-Wails/internal/app.AppVersion=${DEV_VERSION}" else diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b146317..f97c3cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -237,6 +237,7 @@ jobs: shell: bash run: | set -euo pipefail + ./tools/generate-driver-agent-revisions.sh --platform "${{ matrix.platform }}" if [ -n "${{ matrix.wails_tags }}" ]; then wails build -platform ${{ matrix.platform }} -clean -o ${{ matrix.build_name }} -tags "${{ matrix.wails_tags }}" -ldflags "-s -w -X GoNavi-Wails/internal/app.AppVersion=${{ github.ref_name }}" else diff --git a/build-driver-agents.sh b/build-driver-agents.sh index e3734d2..455f72d 100755 --- a/build-driver-agents.sh +++ b/build-driver-agents.sh @@ -152,6 +152,8 @@ echo "🚀 开始构建 optional-driver-agent" echo " 平台:$goos/$goarch" echo " 输出目录:$output_dir_abs" echo " 驱动列表:${drivers[*]}" +echo "🧭 生成 driver-agent revision 指纹" +"$SCRIPT_DIR/tools/generate-driver-agent-revisions.sh" --platform "$target_platform" for driver in "${drivers[@]}"; do if [[ "$driver" == "duckdb" && "$goos" == "windows" && "$goarch" != "amd64" ]]; then diff --git a/build-release.sh b/build-release.sh index edb9fe3..a022e7e 100755 --- a/build-release.sh +++ b/build-release.sh @@ -155,6 +155,7 @@ package_macos_release() { local archive_suffix="$2" echo -e "${GREEN}🍎 正在构建 macOS (${platform})...${NC}" + generate_driver_agent_revisions "darwin/${platform}" wails build -platform "darwin/${platform}" -clean -ldflags "$LDFLAGS" if [ $? -ne 0 ]; then echo -e "${RED} ❌ macOS ${platform} 构建失败。${NC}" @@ -185,6 +186,12 @@ package_macos_release() { echo " ✅ 已生成 $zip_name" } +generate_driver_agent_revisions() { + local platform="$1" + echo " 🧭 正在生成 driver-agent revision 指纹 (${platform})..." + ./tools/generate-driver-agent-revisions.sh --platform "$platform" +} + echo -e "${GREEN}🚀 开始构建 $APP_NAME $VERSION...${NC}" # 清理并创建输出目录 @@ -197,6 +204,7 @@ package_macos_release "amd64" "mac-amd64" # --- Windows AMD64 构建 --- echo -e "${GREEN}🪟 正在构建 Windows (amd64)...${NC}" if command -v x86_64-w64-mingw32-gcc &> /dev/null; then + generate_driver_agent_revisions "windows/amd64" wails build -platform windows/amd64 -clean -ldflags "$LDFLAGS" if [ $? -eq 0 ]; then TARGET_EXE="$DIST_DIR/${APP_NAME}-${VERSION}-windows-amd64.exe" @@ -213,6 +221,7 @@ fi # --- Windows ARM64 构建 --- echo -e "${GREEN}🪟 正在构建 Windows (arm64)...${NC}" if command -v aarch64-w64-mingw32-gcc &> /dev/null; then + generate_driver_agent_revisions "windows/arm64" wails build -platform windows/arm64 -clean -ldflags "$LDFLAGS" if [ $? -eq 0 ]; then TARGET_EXE="$DIST_DIR/${APP_NAME}-${VERSION}-windows-arm64.exe" @@ -235,6 +244,7 @@ CURRENT_ARCH=$(uname -m) if [ "$CURRENT_OS" = "Linux" ] && [ "$CURRENT_ARCH" = "x86_64" ]; then # 本机 Linux amd64,直接构建 + generate_driver_agent_revisions "linux/amd64" wails build -platform linux/amd64 -clean -ldflags "$LDFLAGS" if [ $? -eq 0 ]; then TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64" @@ -255,6 +265,7 @@ elif command -v x86_64-linux-gnu-gcc &> /dev/null; then export CC=x86_64-linux-gnu-gcc export CXX=x86_64-linux-gnu-g++ export CGO_ENABLED=1 + generate_driver_agent_revisions "linux/amd64" wails build -platform linux/amd64 -clean -ldflags "$LDFLAGS" if [ $? -eq 0 ]; then TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64" @@ -279,6 +290,7 @@ fi echo -e "${GREEN}🐧 正在构建 Linux (arm64)...${NC}" if [ "$CURRENT_OS" = "Linux" ] && [ "$CURRENT_ARCH" = "aarch64" ]; then # 本机 Linux arm64,直接构建 + generate_driver_agent_revisions "linux/arm64" wails build -platform linux/arm64 -clean -ldflags "$LDFLAGS" if [ $? -eq 0 ]; then TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64" @@ -298,6 +310,7 @@ elif command -v aarch64-linux-gnu-gcc &> /dev/null; then export CC=aarch64-linux-gnu-gcc export CXX=aarch64-linux-gnu-g++ export CGO_ENABLED=1 + generate_driver_agent_revisions "linux/arm64" wails build -platform linux/arm64 -clean -ldflags "$LDFLAGS" if [ $? -eq 0 ]; then TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64" diff --git a/internal/db/driver_agent_revisions_gen.go b/internal/db/driver_agent_revisions_gen.go new file mode 100644 index 0000000..3db335b --- /dev/null +++ b/internal/db/driver_agent_revisions_gen.go @@ -0,0 +1,21 @@ +// Code generated by tools/generate-driver-agent-revisions.sh; DO NOT EDIT. + +package db + +func init() { + optionalDriverAgentRevisions = map[string]string{ + "mariadb": "src-d6c5c6717338834c", + "diros": "src-ed4f0f64ed28d3fa", + "sphinx": "src-f52324f0a812d7c8", + "sqlserver": "src-ec165f18de9cd8b3", + "sqlite": "src-9dea6c76bc931114", + "duckdb": "src-14027ac1de3c50c7", + "dameng": "src-1a08880ff5bbcf31", + "kingbase": "src-28eed0e4d942b724", + "highgo": "src-76146bf97f07f25c", + "vastbase": "src-555b60c4863542b6", + "mongodb": "src-2540a7350c4243aa", + "tdengine": "src-ce3e4a9c46f6b92d", + "clickhouse": "src-78e5ada4da56704d", + } +} diff --git a/internal/db/driver_support.go b/internal/db/driver_support.go index c2d6acf..e8559cd 100644 --- a/internal/db/driver_support.go +++ b/internal/db/driver_support.go @@ -36,6 +36,11 @@ var optionalGoDrivers = map[string]struct{}{ "clickhouse": {}, } +// optionalDriverAgentRevisions 记录 GoNavi 对各可选 driver-agent 包装逻辑的兼容版本。 +// 该 map 由 tools/generate-driver-agent-revisions.sh 按 driver-agent 源码依赖自动生成, +// 避免人工判断需要 bump 哪个驱动 revision。 +var optionalDriverAgentRevisions = map[string]string{} + var ( externalDriverDirMu sync.RWMutex externalDriverDir string @@ -105,6 +110,10 @@ func IsOptionalGoDriverBuildIncluded(driverType string) bool { return optionalGoDriverBuildIncluded(normalizeRuntimeDriverType(driverType)) } +func OptionalDriverAgentRevision(driverType string) string { + return strings.TrimSpace(optionalDriverAgentRevisions[normalizeRuntimeDriverType(driverType)]) +} + // IsBuiltinDriver 返回指定驱动类型是否为核心内置驱动(始终可用,无需安装)。 func IsBuiltinDriver(driverType string) bool { _, ok := coreBuiltinDrivers[normalizeRuntimeDriverType(driverType)] diff --git a/internal/db/driver_support_test.go b/internal/db/driver_support_test.go index 002fba0..a4efb46 100644 --- a/internal/db/driver_support_test.go +++ b/internal/db/driver_support_test.go @@ -31,6 +31,17 @@ func TestBuiltinLikeDriversRemainAvailable(t *testing.T) { } } +func TestOptionalDriverAgentRevisionsGeneratedForOptionalDrivers(t *testing.T) { + for driverType := range optionalGoDrivers { + if revision := OptionalDriverAgentRevision(driverType); revision == "" { + t.Fatalf("%s 缺少自动生成的 driver-agent revision", driverType) + } + } + if OptionalDriverAgentRevision("doris") != OptionalDriverAgentRevision("diros") { + t.Fatalf("doris/diros revision 应归一一致") + } +} + func TestManagedDriverRequiresInstallMarker(t *testing.T) { tmpDir := t.TempDir() SetExternalDriverDownloadDirectory(tmpDir) diff --git a/tools/generate-driver-agent-revisions.sh b/tools/generate-driver-agent-revisions.sh new file mode 100755 index 0000000..d1bafd3 --- /dev/null +++ b/tools/generate-driver-agent-revisions.sh @@ -0,0 +1,251 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$SCRIPT_DIR" + +DEFAULT_DRIVERS=(mariadb diros sphinx sqlserver sqlite duckdb dameng kingbase highgo vastbase mongodb tdengine clickhouse) +OUTPUT_FILE="internal/db/driver_agent_revisions_gen.go" + +usage() { + cat <<'EOF' +用法: + ./tools/generate-driver-agent-revisions.sh [选项] + +选项: + --platform 按目标平台解析 Go build tags,默认使用当前 Go 环境 + --drivers <列表> 指定驱动列表(逗号分隔),默认生成所有 optional driver + -h, --help 显示帮助 +EOF +} + +normalize_driver() { + local value + value="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" + case "$value" in + doris|diros) echo "diros" ;; + mariadb|diros|sphinx|sqlserver|sqlite|duckdb|dameng|kingbase|highgo|vastbase|mongodb|tdengine|clickhouse) + echo "$value" + ;; + *) + return 1 + ;; + esac +} + +build_driver_name() { + echo "$1" +} + +hash_file() { + local target="$1" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$target" | awk '{print $1}' + return + fi + if command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$target" | awk '{print $1}' + return + fi + echo "未找到 sha256sum 或 shasum" >&2 + exit 1 +} + +should_include_internal_db_file() { + local driver="$1" + local identity="$2" + + case "$identity" in + internal/db/agent_process_stub.go|\ +internal/db/agent_process_windows.go|\ +internal/db/database.go|\ +internal/db/database_optional_factories_full.go|\ +internal/db/database_optional_factories_lite.go|\ +internal/db/driver_agent_binary_check.go|\ +internal/db/driver_support.go|\ +internal/db/json_decode.go|\ +internal/db/mysql_agent_path.go|\ +internal/db/optional_driver_agent_impl.go|\ +internal/db/optional_driver_build_full.go|\ +internal/db/optional_driver_build_lite.go|\ +internal/db/query_value.go|\ +internal/db/scan_rows.go|\ +internal/db/ssl_mode.go|\ +internal/db/timeout.go) + return 0 + ;; + esac + + case "$driver:$identity" in + mariadb:internal/db/mariadb_impl.go|\ +diros:internal/db/diros_impl.go|\ +diros:internal/db/mysql_impl.go|\ +sphinx:internal/db/sphinx_impl.go|\ +sphinx:internal/db/mysql_impl.go|\ +sqlserver:internal/db/sqlserver_impl.go|\ +sqlite:internal/db/sqlite_impl.go|\ +duckdb:internal/db/duckdb_impl.go|\ +duckdb:internal/db/duckdb_driver_import.go|\ +duckdb:internal/db/duckdb_platform_supported.go|\ +duckdb:internal/db/duckdb_platform_unsupported.go|\ +dameng:internal/db/dameng_impl.go|\ +dameng:internal/db/dameng_metadata.go|\ +kingbase:internal/db/kingbase_impl.go|\ +kingbase:internal/db/kingbase_identifier_utils.go|\ +highgo:internal/db/highgo_impl.go|\ +vastbase:internal/db/vastbase_impl.go|\ +mongodb:internal/db/mongodb_impl.go|\ +mongodb:internal/db/mongodb_impl_v1.go|\ +tdengine:internal/db/tdengine_impl.go|\ +clickhouse:internal/db/clickhouse_impl.go) + return 0 + ;; + esac + + return 1 +} + +should_include_source_file() { + local driver="$1" + local identity="$2" + if [[ "$identity" == internal/db/* ]]; then + should_include_internal_db_file "$driver" "$identity" + return + fi + return 0 +} + +target_platform="" +driver_csv="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --platform) + target_platform="${2:-}" + shift 2 + ;; + --drivers) + driver_csv="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "未知参数:$1" >&2 + usage + exit 1 + ;; + esac +done + +if ! command -v go >/dev/null 2>&1; then + echo "未找到 Go,请先安装 Go 并确保 go 在 PATH 中。" >&2 + exit 1 +fi + +if [[ -z "$target_platform" ]]; then + target_platform="$(go env GOOS)/$(go env GOARCH)" +fi +if [[ "$target_platform" != */* ]]; then + echo "--platform 参数格式错误,应为 GOOS/GOARCH,例如 darwin/arm64" >&2 + exit 1 +fi + +goos="${target_platform%%/*}" +goarch="${target_platform##*/}" +gomodcache="$(go env GOMODCACHE)" + +declare -a drivers=() +if [[ -n "$driver_csv" ]]; then + IFS=',' read -r -a raw_drivers <<<"$driver_csv" + for item in "${raw_drivers[@]}"; do + drivers+=("$(normalize_driver "$item")") + done +else + drivers=("${DEFAULT_DRIVERS[@]}") +fi + +fingerprint_driver() { + local driver="$1" + local build_driver tag cgo_enabled tmp file identity file_hash revision + build_driver="$(build_driver_name "$driver")" + tag="gonavi_${build_driver}_driver" + cgo_enabled=0 + if [[ "$driver" == "duckdb" ]]; then + cgo_enabled=1 + fi + + tmp="$(mktemp "${TMPDIR:-/tmp}/gonavi-agent-revision.XXXXXX")" + { + printf 'driver=%s\n' "$driver" + printf 'build_tag=%s\n' "$tag" + printf 'goos=%s\n' "$goos" + printf 'goarch=%s\n' "$goarch" + } >"$tmp" + + while IFS= read -r file; do + [[ -n "$file" && -f "$file" ]] || continue + case "$file" in + "$SCRIPT_DIR"/*) + identity="${file#$SCRIPT_DIR/}" + ;; + "$gomodcache"/*) + identity="gomod/${file#$gomodcache/}" + ;; + *) + identity="$file" + ;; + esac + if [[ "$identity" == "$OUTPUT_FILE" ]]; then + continue + fi + if ! should_include_source_file "$driver" "$identity"; then + continue + fi + file_hash="$(hash_file "$file")" + printf '%s %s\n' "$file_hash" "$identity" + done < <( + CGO_ENABLED="$cgo_enabled" GOOS="$goos" GOARCH="$goarch" GOTOOLCHAIN=auto \ + go list -deps \ + -tags "$tag" \ + -f '{{if not .Standard}}{{range .GoFiles}}{{$.Dir}}/{{.}}{{"\n"}}{{end}}{{range .CgoFiles}}{{$.Dir}}/{{.}}{{"\n"}}{{end}}{{end}}' \ + ./cmd/optional-driver-agent | sort -u + ) >>"$tmp" + + revision="$(hash_file "$tmp" | cut -c1-16)" + rm -f "$tmp" + printf 'src-%s' "$revision" +} + +tmp_output="$(mktemp "${TMPDIR:-/tmp}/gonavi-agent-revisions-go.XXXXXX")" +{ + cat <<'EOF' +// Code generated by tools/generate-driver-agent-revisions.sh; DO NOT EDIT. + +package db + +func init() { + optionalDriverAgentRevisions = map[string]string{ +EOF + for driver in "${drivers[@]}"; do + revision="$(fingerprint_driver "$driver")" + printf '\t\t"%s": "%s",\n' "$driver" "$revision" + done + cat <<'EOF' + } +} +EOF +} >"$tmp_output" + +gofmt -w "$tmp_output" + +if [[ -f "$OUTPUT_FILE" ]] && cmp -s "$tmp_output" "$OUTPUT_FILE"; then + rm -f "$tmp_output" +else + mv "$tmp_output" "$OUTPUT_FILE" +fi + +echo "已生成 driver-agent revisions: $OUTPUT_FILE ($goos/$goarch)"