🐛 fix(ci/driver): 禁用 Windows AMD 驱动 UPX 压缩

- 在驱动产物压缩脚本中跳过 windows/amd64 平台
- 保留 Linux 驱动产物 UPX 压缩与 metadata 自检逻辑
- 补充 Windows amd64 跳过压缩且文件不变的脚本测试
This commit is contained in:
Syngnat
2026-06-12 17:00:09 +08:00
parent 5fcc04a200
commit 356baa1e38
12 changed files with 478 additions and 154 deletions

View File

@@ -601,55 +601,72 @@ jobs:
echo "⚠️ 跳过 DuckDB driver当前平台 ${GOOS}/${GOARCH} 不受支持,仅支持 windows/amd64"
continue
fi
TAG="gonavi_${BUILD_DRIVER}_driver"
BUILD_TAGS="$TAG"
OUTPUT="${DRIVER}-driver-agent-${GOOS}-${GOARCH}"
if [ "$GOOS" = "windows" ]; then
OUTPUT="${OUTPUT}.exe"
DRIVER_VARIANTS=("")
if [ "$DRIVER" = "mongodb" ]; then
DRIVER_VARIANTS=("v1" "v2" "")
fi
OUTPUT_PATH="${OUTDIR}/${OUTPUT}"
DUCKDB_LIB_DIR=""
if [ "$DRIVER" = "duckdb" ] && [ "$GOOS" = "windows" ] && [ "$GOARCH" = "amd64" ]; then
DUCKDB_LIB_DIR="$(prepare_duckdb_windows_library)"
BUILD_TAGS="${BUILD_TAGS} duckdb_use_lib"
fi
echo "🔧 构建 ${OUTPUT_PATH} (tags=${BUILD_TAGS})"
if [ "$DRIVER" = "duckdb" ]; then
if [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_LIB_DIR_GCC="$(cygpath -m "$DUCKDB_LIB_DIR")"
DUCKDB_LIB_DIR_PATH="$(cygpath -u "$DUCKDB_LIB_DIR")"
# cgo 会把每个 CGO_LDFLAGS 片段转成 //go:cgo_ldflag-L 参数不能再额外包引号。
DUCKDB_WINDOWS_CGO_LDFLAGS="-L${DUCKDB_LIB_DIR_GCC} -lduckdb -lstdc++ -lm -lws2_32 -lwsock32 -lrstrtmgr"
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" CGO_LDFLAGS="${DUCKDB_WINDOWS_CGO_LDFLAGS}" PATH="${DUCKDB_LIB_DIR_PATH}:$PATH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
cp "$DUCKDB_LIB_DIR/duckdb.dll" "$OUTDIR/duckdb.dll"
bash ./tools/compress-driver-artifact.sh "$OUTDIR/duckdb.dll" "$TARGET_PLATFORM" "${{ matrix.os_name }}/duckdb.dll"
for DRIVER_VARIANT in "${DRIVER_VARIANTS[@]}"; do
TAG="gonavi_${BUILD_DRIVER}_driver"
BUILD_TAGS="$TAG"
OUTPUT_STEM="${DRIVER}-driver-agent"
if [ "$DRIVER" = "mongodb" ]; then
if [ "$DRIVER_VARIANT" = "v1" ]; then
BUILD_TAGS="gonavi_mongodb_driver_v1"
OUTPUT_STEM="mongodb-driver-agent-v1"
elif [ "$DRIVER_VARIANT" = "v2" ]; then
BUILD_TAGS="gonavi_mongodb_driver"
OUTPUT_STEM="mongodb-driver-agent-v2"
else
BUILD_TAGS="gonavi_mongodb_driver"
fi
fi
OUTPUT="${OUTPUT_STEM}-${GOOS}-${GOARCH}"
if [ "$GOOS" = "windows" ]; then
OUTPUT="${OUTPUT}.exe"
fi
OUTPUT_PATH="${OUTDIR}/${OUTPUT}"
DUCKDB_LIB_DIR=""
if [ "$DRIVER" = "duckdb" ] && [ "$GOOS" = "windows" ] && [ "$GOARCH" = "amd64" ]; then
DUCKDB_LIB_DIR="$(prepare_duckdb_windows_library)"
BUILD_TAGS="${BUILD_TAGS} duckdb_use_lib"
fi
echo "🔧 构建 ${OUTPUT_PATH} (tags=${BUILD_TAGS})"
if [ "$DRIVER" = "duckdb" ]; then
if [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_LIB_DIR_GCC="$(cygpath -m "$DUCKDB_LIB_DIR")"
DUCKDB_LIB_DIR_PATH="$(cygpath -u "$DUCKDB_LIB_DIR")"
# cgo 会把每个 CGO_LDFLAGS 片段转成 //go:cgo_ldflag-L 参数不能再额外包引号。
DUCKDB_WINDOWS_CGO_LDFLAGS="-L${DUCKDB_LIB_DIR_GCC} -lduckdb -lstdc++ -lm -lws2_32 -lwsock32 -lrstrtmgr"
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" CGO_LDFLAGS="${DUCKDB_WINDOWS_CGO_LDFLAGS}" PATH="${DUCKDB_LIB_DIR_PATH}:$PATH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
cp "$DUCKDB_LIB_DIR/duckdb.dll" "$OUTDIR/duckdb.dll"
bash ./tools/compress-driver-artifact.sh "$OUTDIR/duckdb.dll" "$TARGET_PLATFORM" "${{ matrix.os_name }}/duckdb.dll"
else
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
fi
else
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" go build \
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
fi
else
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
fi
bash ./tools/compress-driver-artifact.sh "${OUTPUT_PATH}" "$TARGET_PLATFORM" "${{ matrix.os_name }}/${OUTPUT}"
if [ "$DRIVER" = "duckdb" ] && [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_ZIP_PATH="${OUTDIR}/duckdb-driver.zip"
export DUCKDB_ZIP_PATH
export DUCKDB_AGENT_PATH="${OUTPUT_PATH}"
export DUCKDB_DLL_PATH="${OUTDIR}/duckdb.dll"
bash ./tools/compress-driver-artifact.sh "${OUTPUT_PATH}" "$TARGET_PLATFORM" "${{ matrix.os_name }}/${OUTPUT}"
if [ "$DRIVER" = "duckdb" ] && [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_ZIP_PATH="${OUTDIR}/duckdb-driver.zip"
export DUCKDB_ZIP_PATH
export DUCKDB_AGENT_PATH="${OUTPUT_PATH}"
export DUCKDB_DLL_PATH="${OUTDIR}/duckdb.dll"
python3 - <<'PY'
import os
import zipfile
@@ -666,8 +683,10 @@ jobs:
raise FileNotFoundError(src)
zf.write(src, arcname)
PY
echo "📦 已生成 DuckDB Windows 专属驱动包: ${DUCKDB_ZIP_PATH}"
echo "📦 已生成 DuckDB Windows 专属驱动包: ${DUCKDB_ZIP_PATH}"
fi
fi
done
done
bash ./tools/verify-driver-agent-revisions.sh \

View File

@@ -599,55 +599,72 @@ jobs:
echo "⚠️ 跳过 DuckDB driver当前平台 ${GOOS}/${GOARCH} 不受支持,仅支持 windows/amd64"
continue
fi
TAG="gonavi_${BUILD_DRIVER}_driver"
BUILD_TAGS="$TAG"
OUTPUT="${DRIVER}-driver-agent-${GOOS}-${GOARCH}"
if [ "$GOOS" = "windows" ]; then
OUTPUT="${OUTPUT}.exe"
DRIVER_VARIANTS=("")
if [ "$DRIVER" = "mongodb" ]; then
DRIVER_VARIANTS=("v1" "v2" "")
fi
OUTPUT_PATH="${OUTDIR}/${OUTPUT}"
DUCKDB_LIB_DIR=""
if [ "$DRIVER" = "duckdb" ] && [ "$GOOS" = "windows" ] && [ "$GOARCH" = "amd64" ]; then
DUCKDB_LIB_DIR="$(prepare_duckdb_windows_library)"
BUILD_TAGS="${BUILD_TAGS} duckdb_use_lib"
fi
echo "🔧 构建 ${OUTPUT_PATH} (tags=${BUILD_TAGS})"
if [ "$DRIVER" = "duckdb" ]; then
if [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_LIB_DIR_GCC="$(cygpath -m "$DUCKDB_LIB_DIR")"
DUCKDB_LIB_DIR_PATH="$(cygpath -u "$DUCKDB_LIB_DIR")"
# cgo 会把每个 CGO_LDFLAGS 片段转成 //go:cgo_ldflag-L 参数不能再额外包引号。
DUCKDB_WINDOWS_CGO_LDFLAGS="-L${DUCKDB_LIB_DIR_GCC} -lduckdb -lstdc++ -lm -lws2_32 -lwsock32 -lrstrtmgr"
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" CGO_LDFLAGS="${DUCKDB_WINDOWS_CGO_LDFLAGS}" PATH="${DUCKDB_LIB_DIR_PATH}:$PATH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
cp "$DUCKDB_LIB_DIR/duckdb.dll" "$OUTDIR/duckdb.dll"
bash ./tools/compress-driver-artifact.sh "$OUTDIR/duckdb.dll" "$TARGET_PLATFORM" "${{ matrix.os_name }}/duckdb.dll"
for DRIVER_VARIANT in "${DRIVER_VARIANTS[@]}"; do
TAG="gonavi_${BUILD_DRIVER}_driver"
BUILD_TAGS="$TAG"
OUTPUT_STEM="${DRIVER}-driver-agent"
if [ "$DRIVER" = "mongodb" ]; then
if [ "$DRIVER_VARIANT" = "v1" ]; then
BUILD_TAGS="gonavi_mongodb_driver_v1"
OUTPUT_STEM="mongodb-driver-agent-v1"
elif [ "$DRIVER_VARIANT" = "v2" ]; then
BUILD_TAGS="gonavi_mongodb_driver"
OUTPUT_STEM="mongodb-driver-agent-v2"
else
BUILD_TAGS="gonavi_mongodb_driver"
fi
fi
OUTPUT="${OUTPUT_STEM}-${GOOS}-${GOARCH}"
if [ "$GOOS" = "windows" ]; then
OUTPUT="${OUTPUT}.exe"
fi
OUTPUT_PATH="${OUTDIR}/${OUTPUT}"
DUCKDB_LIB_DIR=""
if [ "$DRIVER" = "duckdb" ] && [ "$GOOS" = "windows" ] && [ "$GOARCH" = "amd64" ]; then
DUCKDB_LIB_DIR="$(prepare_duckdb_windows_library)"
BUILD_TAGS="${BUILD_TAGS} duckdb_use_lib"
fi
echo "🔧 构建 ${OUTPUT_PATH} (tags=${BUILD_TAGS})"
if [ "$DRIVER" = "duckdb" ]; then
if [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_LIB_DIR_GCC="$(cygpath -m "$DUCKDB_LIB_DIR")"
DUCKDB_LIB_DIR_PATH="$(cygpath -u "$DUCKDB_LIB_DIR")"
# cgo 会把每个 CGO_LDFLAGS 片段转成 //go:cgo_ldflag-L 参数不能再额外包引号。
DUCKDB_WINDOWS_CGO_LDFLAGS="-L${DUCKDB_LIB_DIR_GCC} -lduckdb -lstdc++ -lm -lws2_32 -lwsock32 -lrstrtmgr"
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" CGO_LDFLAGS="${DUCKDB_WINDOWS_CGO_LDFLAGS}" PATH="${DUCKDB_LIB_DIR_PATH}:$PATH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
cp "$DUCKDB_LIB_DIR/duckdb.dll" "$OUTDIR/duckdb.dll"
bash ./tools/compress-driver-artifact.sh "$OUTDIR/duckdb.dll" "$TARGET_PLATFORM" "${{ matrix.os_name }}/duckdb.dll"
else
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
fi
else
CGO_ENABLED=1 GOOS="$GOOS" GOARCH="$GOARCH" go build \
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
fi
else
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" go build \
-tags "${BUILD_TAGS}" \
-trimpath \
-ldflags "-s -w" \
-o "${OUTPUT_PATH}" \
./cmd/optional-driver-agent
fi
bash ./tools/compress-driver-artifact.sh "${OUTPUT_PATH}" "$TARGET_PLATFORM" "${{ matrix.os_name }}/${OUTPUT}"
if [ "$DRIVER" = "duckdb" ] && [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_ZIP_PATH="${OUTDIR}/duckdb-driver.zip"
export DUCKDB_ZIP_PATH
export DUCKDB_AGENT_PATH="${OUTPUT_PATH}"
export DUCKDB_DLL_PATH="${OUTDIR}/duckdb.dll"
bash ./tools/compress-driver-artifact.sh "${OUTPUT_PATH}" "$TARGET_PLATFORM" "${{ matrix.os_name }}/${OUTPUT}"
if [ "$DRIVER" = "duckdb" ] && [ -n "$DUCKDB_LIB_DIR" ]; then
DUCKDB_ZIP_PATH="${OUTDIR}/duckdb-driver.zip"
export DUCKDB_ZIP_PATH
export DUCKDB_AGENT_PATH="${OUTPUT_PATH}"
export DUCKDB_DLL_PATH="${OUTDIR}/duckdb.dll"
python3 - <<'PY'
import os
import zipfile
@@ -664,8 +681,10 @@ jobs:
raise FileNotFoundError(src)
zf.write(src, arcname)
PY
echo "📦 已生成 DuckDB Windows 专属驱动包: ${DUCKDB_ZIP_PATH}"
echo "📦 已生成 DuckDB Windows 专属驱动包: ${DUCKDB_ZIP_PATH}"
fi
fi
done
done
bash ./tools/verify-driver-agent-revisions.sh \

View File

@@ -2812,7 +2812,7 @@ func readInstalledDriverPackage(downloadDir string, driverType string) (installe
func optionalDriverAgentRevisionStatus(driverType string, pkg installedDriverPackage, packageMetaExists bool) (bool, string, string) {
expected := db.OptionalDriverAgentRevision(driverType)
if strings.TrimSpace(expected) == "" || !packageMetaExists || !db.IsOptionalGoDriver(driverType) {
if strings.TrimSpace(expected) == "" || !packageMetaExists || !db.IsOptionalGoDriver(driverType) || !shouldVerifyOptionalDriverAgentRevision(driverType, pkg.Version) {
return false, "", expected
}
actual := strings.TrimSpace(pkg.AgentRevision)
@@ -4139,7 +4139,7 @@ func withEnvValue(env []string, key string, value string) []string {
}
func duckDBWindowsDynamicLibraryCGOLDFlags(libDir string) string {
normalizedDir := filepath.ToSlash(strings.TrimSpace(libDir))
normalizedDir := strings.ReplaceAll(filepath.ToSlash(strings.TrimSpace(libDir)), `\`, `/`)
parts := []string{
// cgo 会把每个 CGO_LDFLAGS 片段转成 //go:cgo_ldflag带引号的 -L 在 windows/amd64 上会被当成非法参数。
fmt.Sprintf("-L%s", normalizedDir),
@@ -4391,10 +4391,9 @@ func optionalDriverNameStemCandidates(driverType string, selectedVersion string)
switch resolveMongoDriverMajorFromVersion(selectedVersion) {
case 1:
appendStem(base + "-v1")
appendStem(base)
case 2:
appendStem(base)
appendStem(base + "-v2")
appendStem(base)
default:
appendStem(base)
}

View File

@@ -52,6 +52,22 @@ func TestOptionalDriverPackageUpdateStatusDetectsMongoV2WhenLegacyDefault(t *tes
}
}
func TestOptionalDriverPackageUpdateStatusAcceptsMongoV1WithoutRevision(t *testing.T) {
definition, ok := resolveDriverDefinition("mongodb")
if !ok {
t.Fatal("expected mongodb driver definition")
}
meta := installedDriverPackage{
Version: "1.17.9",
AgentRevision: "",
}
needsUpdate, reason, _ := optionalDriverPackageUpdateStatus(definition, meta, true)
if needsUpdate {
t.Fatalf("expected MongoDB v1 driver to skip revision mismatch prompts, reason=%q", reason)
}
}
func TestOptionalDriverAgentRevisionCurrentRejectsStaleMetadata(t *testing.T) {
originalProbe := optionalDriverAgentMetadataProbe
t.Cleanup(func() {

View File

@@ -266,6 +266,58 @@ func TestResolveOptionalDriverAgentDownloadURLsUsesMongoV1AssetForCompatibleDefa
t.Fatalf("expected MongoDB v1 release asset %q in candidates, got %v", want, urls)
}
func TestResolveOptionalDriverAgentDownloadURLsDoesNotUseMongoV2BaseForCompatibleDefault(t *testing.T) {
definition, ok := resolveDriverDefinition("mongodb")
if !ok {
t.Fatal("expected mongodb driver definition")
}
originalVersion := AppVersion
AppVersion = "0.7.9"
t.Cleanup(func() {
AppVersion = originalVersion
})
baseAssetName := optionalDriverReleaseAssetNameForType("mongodb", runtime.GOOS, runtime.GOARCH)
seedReleaseAssetSizeCache(t, "tag:v0.7.9", map[string]int64{
baseAssetName: 24 << 20,
})
seedReleaseAssetSizeCache(t, "latest", map[string]int64{
baseAssetName: 24 << 20,
})
urls := resolveOptionalDriverAgentDownloadURLs(
definition,
"builtin://activate/mongodb",
"1.17.9",
)
for _, got := range urls {
if strings.Contains(got, baseAssetName) {
t.Fatalf("expected MongoDB v1 install not to use ambiguous base asset %q, got %v", baseAssetName, urls)
}
}
}
func TestMongoDBVersionedAssetNamesDoNotFallbackToBaseForV1(t *testing.T) {
v1AssetName := mongoVersionedReleaseAssetName(1)
baseAssetName := optionalDriverReleaseAssetNameForType("mongodb", runtime.GOOS, runtime.GOARCH)
v1Names := optionalDriverReleaseAssetNamesForVersion("mongodb", "1.17.9")
if len(v1Names) != 1 || v1Names[0] != v1AssetName {
t.Fatalf("expected MongoDB v1 to use only %q, got %v", v1AssetName, v1Names)
}
for _, name := range v1Names {
if name == baseAssetName {
t.Fatalf("MongoDB v1 must not fallback to ambiguous base asset %q", baseAssetName)
}
}
v2Names := optionalDriverReleaseAssetNamesForVersion("mongodb", "2.5.0")
if len(v2Names) < 2 || v2Names[0] != mongoVersionedReleaseAssetName(2) || v2Names[1] != baseAssetName {
t.Fatalf("expected MongoDB v2 to prefer versioned asset then base compatibility asset, got %v", v2Names)
}
}
func TestResolveOptionalDriverAgentDownloadURLsSkipsBundleOnlyDamengAsset(t *testing.T) {
definition, ok := resolveDriverDefinition("dameng")
if !ok {

View File

@@ -59,7 +59,11 @@ goos="${platform%%/*}"
goarch="${platform##*/}"
case "$goos/$goarch" in
linux/amd64|linux/arm64|windows/amd64)
windows/amd64)
echo " Windows amd64 驱动产物不执行 UPX 压缩:$label"
exit 0
;;
linux/amd64|linux/arm64)
;;
*)
echo " UPX 跳过不支持的平台:$label ($platform)"

View File

@@ -2,16 +2,6 @@
set -euo pipefail
host_platform="$(go env GOOS)/$(go env GOARCH)"
case "$host_platform" in
linux/amd64|linux/arm64|windows/amd64)
;;
*)
echo "skip compress-driver-artifact smoke test on unsupported host platform: $host_platform"
exit 0
;;
esac
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-compress-driver-artifact.XXXXXX")"
cleanup() {
@@ -19,6 +9,30 @@ cleanup() {
}
trap cleanup EXIT
windows_amd64_bin="$tmpdir/windows-driver-agent-windows-amd64.exe"
printf 'fake windows amd64 driver' >"$windows_amd64_bin"
windows_amd64_before="$(cat "$windows_amd64_bin")"
windows_skip_output="$(bash "$repo_root/tools/compress-driver-artifact.sh" "$windows_amd64_bin" "windows/amd64" "windows-amd64-driver" 2>&1)"
if [[ "$windows_skip_output" != *"Windows amd64 驱动产物不执行 UPX 压缩"* ]]; then
echo "expected Windows amd64 driver artifact UPX compression to be skipped" >&2
echo "$windows_skip_output" >&2
exit 1
fi
if [[ "$(cat "$windows_amd64_bin")" != "$windows_amd64_before" ]]; then
echo "expected Windows amd64 driver artifact to remain unchanged when UPX is skipped" >&2
exit 1
fi
host_platform="$(go env GOOS)/$(go env GOARCH)"
case "$host_platform" in
linux/amd64|linux/arm64)
;;
*)
echo "skip compress-driver-artifact smoke test on unsupported host platform: $host_platform"
exit 0
;;
esac
suffix=""
if [[ "$host_platform" == windows/* ]]; then
suffix=".exe"

View File

@@ -19,19 +19,25 @@ def parse_args():
def infer_driver_and_platform(file_name: str):
suffixes = [
"-driver-agent-darwin-amd64",
"-driver-agent-darwin-arm64",
"-driver-agent-linux-amd64",
"-driver-agent-windows-amd64.exe",
"-driver-agent-windows-arm64.exe",
("-driver-agent-v1-darwin-amd64", "darwin/amd64"),
("-driver-agent-v1-darwin-arm64", "darwin/arm64"),
("-driver-agent-v1-linux-amd64", "linux/amd64"),
("-driver-agent-v1-windows-amd64.exe", "windows/amd64"),
("-driver-agent-v1-windows-arm64.exe", "windows/arm64"),
("-driver-agent-v2-darwin-amd64", "darwin/amd64"),
("-driver-agent-v2-darwin-arm64", "darwin/arm64"),
("-driver-agent-v2-linux-amd64", "linux/amd64"),
("-driver-agent-v2-windows-amd64.exe", "windows/amd64"),
("-driver-agent-v2-windows-arm64.exe", "windows/arm64"),
("-driver-agent-darwin-amd64", "darwin/amd64"),
("-driver-agent-darwin-arm64", "darwin/arm64"),
("-driver-agent-linux-amd64", "linux/amd64"),
("-driver-agent-windows-amd64.exe", "windows/amd64"),
("-driver-agent-windows-arm64.exe", "windows/arm64"),
]
for suffix in suffixes:
for suffix, platform in suffixes:
if file_name.endswith(suffix):
driver = file_name[: -len(suffix)]
platform_name = suffix.replace("-driver-agent-", "")
if platform_name.endswith(".exe"):
platform_name = platform_name.removesuffix(".exe")
platform = platform_name.replace("-", "/", 1)
return driver, platform
return None, None

View File

@@ -24,7 +24,7 @@ def expected_revision(revision_file: Path, driver: str):
class GenerateDriverReleaseManifestTest(unittest.TestCase):
def _generate_revision_file(self, platform: str):
def _generate_revision_file(self, platform: str, drivers: str = "clickhouse"):
worktree = Path(tempfile.mkdtemp(prefix="gonavi-release-manifest-worktree-"))
subprocess.run(
["git", "worktree", "add", "--detach", str(worktree), "HEAD"],
@@ -43,7 +43,7 @@ class GenerateDriverReleaseManifestTest(unittest.TestCase):
)
)
subprocess.run(
["bash", "./tools/generate-driver-agent-revisions.sh", "--platform", platform, "--drivers", "clickhouse"],
["bash", "./tools/generate-driver-agent-revisions.sh", "--platform", platform, "--drivers", drivers],
cwd=worktree,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
@@ -63,6 +63,8 @@ class GenerateDriverReleaseManifestTest(unittest.TestCase):
assets_dir / "MacOS" / "clickhouse-driver-agent-darwin-arm64": b"darwin-binary",
assets_dir / "Linux" / "clickhouse-driver-agent-linux-amd64": b"linux-binary",
assets_dir / "Windows" / "clickhouse-driver-agent-windows-amd64.exe": b"MZfake-binary",
assets_dir / "Windows" / "mongodb-driver-agent-v1-windows-amd64.exe": b"MZfake-mongodb-v1",
assets_dir / "Windows" / "mongodb-driver-agent-v2-windows-amd64.exe": b"MZfake-mongodb-v2",
}
for path, content in fixtures.items():
path.write_bytes(content)
@@ -78,12 +80,12 @@ class GenerateDriverReleaseManifestTest(unittest.TestCase):
check=True,
)
self.assertIn("asset count: 3", proc.stdout)
self.assertIn("asset count: 5", proc.stdout)
manifest = json.loads(output.read_text(encoding="utf-8"))
assets = manifest["assets"]
darwin_revision_file = self._generate_revision_file("darwin/arm64")
linux_revision_file = self._generate_revision_file("linux/amd64")
windows_revision_file = self._generate_revision_file("windows/amd64")
windows_revision_file = self._generate_revision_file("windows/amd64", "clickhouse,mongodb")
self.assertEqual(
assets["clickhouse-driver-agent-darwin-arm64"]["revision"],
expected_revision(darwin_revision_file, "clickhouse"),
@@ -96,6 +98,22 @@ class GenerateDriverReleaseManifestTest(unittest.TestCase):
assets["clickhouse-driver-agent-windows-amd64.exe"]["revision"],
expected_revision(windows_revision_file, "clickhouse"),
)
self.assertEqual(
assets["mongodb-driver-agent-v1-windows-amd64.exe"]["driver"],
"mongodb",
)
self.assertEqual(
assets["mongodb-driver-agent-v1-windows-amd64.exe"]["platform"],
"windows/amd64",
)
self.assertEqual(
assets["mongodb-driver-agent-v1-windows-amd64.exe"]["revision"],
expected_revision(windows_revision_file, "mongodb"),
)
self.assertEqual(
assets["mongodb-driver-agent-v2-windows-amd64.exe"]["driver"],
"mongodb",
)
if __name__ == "__main__":

View File

@@ -4,6 +4,7 @@ import argparse
import hashlib
import json
import os
import platform
import shutil
import stat
import subprocess
@@ -61,6 +62,16 @@ def asset_map(release: dict):
return result
def asset_sha256_digest(asset: dict):
digest = str(asset.get("digest") or "").strip().lower()
prefix = "sha256:"
if digest.startswith(prefix):
value = digest[len(prefix) :].strip()
if value:
return value
return ""
def infer_asset_path(name: str):
trimmed = str(name or "").strip()
if not trimmed:
@@ -69,15 +80,58 @@ def infer_asset_path(name: str):
return "Windows/duckdb.dll"
if trimmed == "duckdb-driver.zip":
return None
if trimmed.endswith("-driver-agent-windows-amd64.exe") or trimmed.endswith("-driver-agent-windows-arm64.exe"):
if (
trimmed.endswith("-driver-agent-windows-amd64.exe")
or trimmed.endswith("-driver-agent-windows-arm64.exe")
or trimmed.endswith("-driver-agent-v1-windows-amd64.exe")
or trimmed.endswith("-driver-agent-v1-windows-arm64.exe")
or trimmed.endswith("-driver-agent-v2-windows-amd64.exe")
or trimmed.endswith("-driver-agent-v2-windows-arm64.exe")
):
return f"Windows/{trimmed}"
if trimmed.endswith("-driver-agent-darwin-amd64") or trimmed.endswith("-driver-agent-darwin-arm64"):
if (
trimmed.endswith("-driver-agent-darwin-amd64")
or trimmed.endswith("-driver-agent-darwin-arm64")
or trimmed.endswith("-driver-agent-v1-darwin-amd64")
or trimmed.endswith("-driver-agent-v1-darwin-arm64")
or trimmed.endswith("-driver-agent-v2-darwin-amd64")
or trimmed.endswith("-driver-agent-v2-darwin-arm64")
):
return f"MacOS/{trimmed}"
if trimmed.endswith("-driver-agent-linux-amd64"):
if (
trimmed.endswith("-driver-agent-linux-amd64")
or trimmed.endswith("-driver-agent-v1-linux-amd64")
or trimmed.endswith("-driver-agent-v2-linux-amd64")
):
return f"Linux/{trimmed}"
return None
def normalize_machine(value: str):
machine = str(value or "").strip().lower()
if machine in {"x86_64", "amd64"}:
return "amd64"
if machine in {"aarch64", "arm64"}:
return "arm64"
return machine
def current_runtime_platform():
system = platform.system().lower()
if system == "darwin":
goos = "darwin"
elif system == "windows":
goos = "windows"
elif system == "linux":
goos = "linux"
else:
return ""
goarch = normalize_machine(platform.machine())
if not goarch:
return ""
return f"{goos}/{goarch}"
def sha256_file(path: Path):
h = hashlib.sha256()
with open(path, "rb") as fh:
@@ -104,12 +158,16 @@ def probe_metadata_revision(path: Path):
return str(((payload.get("data") or {}).get("agentRevision") or "")).strip()
def validate_release_assets(release: dict, manifest: dict):
def validate_release_assets(release: dict, manifest: dict, runtime_platform=None):
assets = asset_map(release)
manifest_assets = manifest.get("assets") or {}
if not isinstance(manifest_assets, dict) or not manifest_assets:
raise RuntimeError("manifest assets is empty")
if runtime_platform is None:
runtime_platform = current_runtime_platform()
runtime_platform = str(runtime_platform or "").strip().lower()
mismatches = []
skipped = []
with tempfile.TemporaryDirectory(prefix="gonavi-release-assets-") as tmp:
@@ -124,20 +182,27 @@ def validate_release_assets(release: dict, manifest: dict):
expected_sha = str(meta.get("sha256") or "").strip().lower()
expected_revision = str(meta.get("revision") or "").strip()
asset_platform = str(meta.get("platform") or "").strip().lower()
local_path = None
actual_sha = asset_sha256_digest(asset)
if expected_sha and not actual_sha:
local_path = tmp_root / name
download_url(str(asset.get("browser_download_url") or "").strip(), local_path)
actual_sha = sha256_file(local_path).lower()
if expected_sha and actual_sha != expected_sha:
mismatches.append((name, "sha256", actual_sha, expected_sha))
continue
path_hint = infer_asset_path(name)
if path_hint is None:
skipped.append(name)
continue
local_path = tmp_root / name
download_url(str(asset.get("browser_download_url") or "").strip(), local_path)
actual_sha = sha256_file(local_path).lower()
if expected_sha and actual_sha != expected_sha:
mismatches.append((name, "sha256", actual_sha, expected_sha))
continue
if expected_revision:
if expected_revision and asset_platform == runtime_platform:
if local_path is None:
local_path = tmp_root / name
download_url(str(asset.get("browser_download_url") or "").strip(), local_path)
actual_revision = probe_metadata_revision(local_path)
if actual_revision != expected_revision:
mismatches.append((name, "revision", actual_revision, expected_revision))

View File

@@ -24,6 +24,14 @@ class ValidateDriverReleaseAssetsTests(unittest.TestCase):
MODULE.infer_asset_path("clickhouse-driver-agent-windows-arm64.exe"),
"Windows/clickhouse-driver-agent-windows-arm64.exe",
)
self.assertEqual(
MODULE.infer_asset_path("mongodb-driver-agent-v1-windows-amd64.exe"),
"Windows/mongodb-driver-agent-v1-windows-amd64.exe",
)
self.assertEqual(
MODULE.infer_asset_path("mongodb-driver-agent-v2-darwin-arm64"),
"MacOS/mongodb-driver-agent-v2-darwin-arm64",
)
self.assertEqual(MODULE.infer_asset_path("duckdb.dll"), "Windows/duckdb.dll")
self.assertIsNone(MODULE.infer_asset_path("duckdb-driver.zip"))
@@ -83,6 +91,7 @@ class ValidateDriverReleaseAssetsTests(unittest.TestCase):
manifest = {
"assets": {
"clickhouse-driver-agent-darwin-arm64": {
"platform": "darwin/arm64",
"revision": "src-expected",
"sha256": hashlib.sha256(payload).hexdigest(),
}
@@ -101,7 +110,7 @@ class ValidateDriverReleaseAssetsTests(unittest.TestCase):
try:
MODULE.download_url = fake_download
MODULE.probe_metadata_revision = lambda _path: "src-actual"
mismatches, skipped = MODULE.validate_release_assets(release, manifest)
mismatches, skipped = MODULE.validate_release_assets(release, manifest, runtime_platform="darwin/arm64")
finally:
MODULE.download_url = original_download
MODULE.probe_metadata_revision = original_probe
@@ -109,6 +118,84 @@ class ValidateDriverReleaseAssetsTests(unittest.TestCase):
self.assertEqual(skipped, [])
self.assertEqual(mismatches, [("clickhouse-driver-agent-darwin-arm64", "revision", "src-actual", "src-expected")])
def test_validate_release_assets_skips_cross_platform_revision_probe(self):
release = {
"assets": [
{
"name": "clickhouse-driver-agent-darwin-amd64",
"browser_download_url": "https://example.test/clickhouse-driver-agent-darwin-amd64",
}
]
}
payload = b"darwin-binary"
manifest = {
"assets": {
"clickhouse-driver-agent-darwin-amd64": {
"platform": "darwin/amd64",
"revision": "src-expected",
"sha256": hashlib.sha256(payload).hexdigest(),
}
}
}
with tempfile.TemporaryDirectory(prefix="gonavi-validate-release-assets-") as tmp:
payload_path = pathlib.Path(tmp) / "clickhouse-driver-agent-darwin-amd64"
payload_path.write_bytes(payload)
def fake_download(url, destination):
destination.write_bytes(payload_path.read_bytes())
def fail_probe(_path):
raise AssertionError("cross-platform asset should not be executed")
original_download = MODULE.download_url
original_probe = MODULE.probe_metadata_revision
try:
MODULE.download_url = fake_download
MODULE.probe_metadata_revision = fail_probe
mismatches, skipped = MODULE.validate_release_assets(release, manifest, runtime_platform="linux/amd64")
finally:
MODULE.download_url = original_download
MODULE.probe_metadata_revision = original_probe
self.assertEqual(mismatches, [])
self.assertEqual(skipped, [])
def test_validate_release_assets_uses_release_digest_without_download(self):
payload = b"darwin-binary"
digest = hashlib.sha256(payload).hexdigest()
release = {
"assets": [
{
"name": "clickhouse-driver-agent-darwin-amd64",
"digest": f"sha256:{digest}",
"browser_download_url": "https://example.test/clickhouse-driver-agent-darwin-amd64",
}
]
}
manifest = {
"assets": {
"clickhouse-driver-agent-darwin-amd64": {
"platform": "darwin/amd64",
"revision": "src-expected",
"sha256": digest,
}
}
}
def fail_download(_url, _destination):
raise AssertionError("release digest should avoid downloading cross-platform assets")
original_download = MODULE.download_url
try:
MODULE.download_url = fail_download
mismatches, skipped = MODULE.validate_release_assets(release, manifest, runtime_platform="linux/amd64")
finally:
MODULE.download_url = original_download
self.assertEqual(mismatches, [])
self.assertEqual(skipped, [])
if __name__ == "__main__":
unittest.main()

View File

@@ -101,7 +101,11 @@ expected_revision_for() {
build_tags_for_driver() {
local driver="$1"
local variant="${2:-}"
local tags="gonavi_${driver}_driver"
if [[ "$driver" == "mongodb" && "$variant" == "v1" ]]; then
tags="gonavi_mongodb_driver_v1"
fi
if [[ "$driver" == "duckdb" && "$host_goos" == "windows" && "$host_goarch" == "amd64" ]]; then
tags="${tags} duckdb_use_lib"
fi
@@ -110,15 +114,29 @@ build_tags_for_driver() {
agent_path_for() {
local driver="$1"
local variant="${2:-}"
local public_name asset
public_name="$(public_driver_name "$driver")"
asset="${public_name}-driver-agent-${goos}-${goarch}"
if [[ "$driver" == "mongodb" && -n "$variant" ]]; then
asset="${public_name}-driver-agent-${variant}-${goos}-${goarch}"
else
asset="${public_name}-driver-agent-${goos}-${goarch}"
fi
if [[ "$goos" == "windows" ]]; then
asset="${asset}.exe"
fi
printf '%s\n' "${assets_dir%/}/${platform_dir}/${asset}"
}
agent_variants_for() {
local driver="$1"
if [[ "$driver" == "mongodb" ]]; then
printf '%s\n' "v1" "v2" ""
return
fi
printf '%s\n' ""
}
probe_agent_revision() {
local agent_path="$1"
local request
@@ -136,8 +154,9 @@ print(data.get("agentRevision", ""))
probe_host_agent_revision() {
local driver="$1"
local variant="${2:-}"
local build_tags probe_dir probe_path revision
build_tags="$(build_tags_for_driver "$driver")"
build_tags="$(build_tags_for_driver "$driver" "$variant")"
probe_dir="$(mktemp -d)"
probe_path="${probe_dir}/probe-agent"
if [[ "$host_goos" == "windows" ]]; then
@@ -241,36 +260,42 @@ for raw_driver in "${raw_drivers[@]}"; do
continue
fi
agent_path="$(agent_path_for "$driver")"
if [[ ! -f "$agent_path" ]]; then
echo "$driver 缺少 driver-agent 资产:$agent_path"
failed=1
continue
fi
chmod +x "$agent_path" 2>/dev/null || true
if [[ "$goos" == "windows" ]]; then
if ! validate_windows_pe_machine "$agent_path" "$goarch"; then
echo "$driver Windows driver-agent 架构校验失败asset=$agent_path target=$target_platform"
while IFS= read -r variant; do
agent_path="$(agent_path_for "$driver" "$variant")"
variant_label="$driver"
if [[ -n "$variant" ]]; then
variant_label="${driver}-${variant}"
fi
if [[ ! -f "$agent_path" ]]; then
echo "$variant_label 缺少 driver-agent 资产:$agent_path"
failed=1
continue
fi
fi
chmod +x "$agent_path" 2>/dev/null || true
actual=""
if can_execute_target_binary; then
actual="$(probe_agent_revision "$agent_path" || true)"
else
echo " runner 平台 ${host_platform} 无法直接执行目标二进制 ${target_platform},已先完成目标资产架构校验,再用 host-native probe 校验相同 build tags 的 revision"
actual="$(probe_host_agent_revision "$driver" || true)"
fi
if [[ "$goos" == "windows" ]]; then
if ! validate_windows_pe_machine "$agent_path" "$goarch"; then
echo "$variant_label Windows driver-agent 架构校验失败asset=$agent_path target=$target_platform"
failed=1
continue
fi
fi
if [[ "$actual" != "$expected" ]]; then
echo "$driver driver-agent revision 不匹配asset=$agent_path actual=${actual:-} expected=$expected"
failed=1
continue
fi
echo "$driver driver-agent revision 校验通过:$actual"
actual=""
if can_execute_target_binary; then
actual="$(probe_agent_revision "$agent_path" || true)"
else
echo " runner 平台 ${host_platform} 无法直接执行目标二进制 ${target_platform},已先完成目标资产架构校验,再用 host-native probe 校验相同 build tags 的 revision"
actual="$(probe_host_agent_revision "$driver" "$variant" || true)"
fi
if [[ "$actual" != "$expected" ]]; then
echo "$variant_label driver-agent revision 不匹配asset=$agent_path actual=${actual:-} expected=$expected"
failed=1
continue
fi
echo "$variant_label driver-agent revision 校验通过:$actual"
done < <(agent_variants_for "$driver")
done
exit "$failed"