diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 8213a56..d2a4a19 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v6 with: - go-version: '1.24' + go-version: '1.24.3' - name: Setup Node uses: actions/setup-node@v5 @@ -70,6 +70,7 @@ jobs: drivers: ${{ steps.detect.outputs.drivers }} has_changes: ${{ steps.detect.outputs.has_changes }} release_source: ${{ steps.detect.outputs.release_source }} + source_commit: ${{ steps.published_source.outputs.source_commit }} steps: - name: Checkout code uses: actions/checkout@v5 @@ -96,6 +97,31 @@ jobs: shell: bash run: | set -euo pipefail + merge_csv() { + local current="$1" + local incoming="$2" + local merged="$current" + local seen=",${current}," + local item + IFS=',' read -r -a items <<< "$incoming" + for item in "${items[@]}"; do + item="$(printf '%s' "$item" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" + if [[ -z "$item" ]]; then + continue + fi + if [[ "$seen" == *",$item,"* ]]; then + continue + fi + if [[ -n "$merged" ]]; then + merged="${merged},${item}" + else + merged="$item" + fi + seen="${seen}${item}," + done + printf '%s\n' "$merged" + } + BASE_REF="${{ steps.published_source.outputs.source_commit }}" if [[ -n "$BASE_REF" ]]; then if git rev-parse --verify "${BASE_REF}^{commit}" >/dev/null 2>&1 && git merge-base --is-ancestor "$BASE_REF" "$GITHUB_SHA"; then @@ -112,6 +138,22 @@ jobs: fi echo "🧭 Final driver detection base: $BASE_REF" DRIVERS="$(bash ./tools/detect-changed-driver-agents.sh --base "$BASE_REF" --head "$GITHUB_SHA")" + if [[ "$BASE_REF" != "all" ]]; then + REVISION_DRIVERS="" + for PLATFORM in darwin/amd64 darwin/arm64 windows/amd64 windows/arm64 linux/amd64; do + PLATFORM_DRIVERS="$(bash ./tools/diff-driver-agent-revisions.sh --base "$BASE_REF" --head "$GITHUB_SHA" --platform "$PLATFORM")" || { + echo "⚠️ 平台 revision 差异对比失败(${PLATFORM}),保守回退为全量驱动重建" + DRIVERS="$(bash ./tools/detect-changed-driver-agents.sh --base all --head "$GITHUB_SHA")" + REVISION_DRIVERS="" + break + } + REVISION_DRIVERS="$(merge_csv "$REVISION_DRIVERS" "$PLATFORM_DRIVERS")" + done + if [[ -n "$REVISION_DRIVERS" ]]; then + echo "🧭 Revision diff union drivers: $REVISION_DRIVERS" + DRIVERS="$(merge_csv "$DRIVERS" "$REVISION_DRIVERS")" + fi + fi echo "drivers=${DRIVERS}" >> "$GITHUB_OUTPUT" if [ -n "$DRIVERS" ]; then echo "has_changes=true" >> "$GITHUB_OUTPUT" @@ -194,7 +236,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v6 with: - go-version: '1.24' + go-version: '1.24.3' - name: Download frontend dist uses: actions/download-artifact@v7 @@ -371,11 +413,42 @@ jobs: wails build -s -skipbindings -platform ${{ matrix.platform }} -clean -o ${{ matrix.build_name }} -ldflags "-s -w -X GoNavi-Wails/internal/app.AppVersion=${DEV_VERSION}" fi - - name: Build Optional Driver Agents + - name: Resolve Platform Driver Revision Diff + id: revision_diff if: ${{ matrix.build_optional_agents && needs.driver_agents.outputs.has_changes == 'true' }} shell: bash + run: | + set -euo pipefail + BASE_REF="${{ needs.driver_agents.outputs.source_commit }}" + FALLBACK_DRIVERS="${{ needs.driver_agents.outputs.drivers }}" + + if [[ -z "$BASE_REF" ]]; then + echo "⚠️ 未拿到已发布 driver release 源码提交,回退使用全局检测结果:${FALLBACK_DRIVERS}" + echo "drivers=${FALLBACK_DRIVERS}" >> "$GITHUB_OUTPUT" + else + echo "🧭 对比当前平台 revision:base=${BASE_REF} head=${GITHUB_SHA} platform=${{ matrix.platform }}" + if DRIVERS="$(bash ./tools/diff-driver-agent-revisions.sh --base "$BASE_REF" --head "$GITHUB_SHA" --platform "${{ matrix.platform }}")"; then + echo "🧭 当前平台实际需要重建的 driver agents: ${DRIVERS:-}" + echo "drivers=${DRIVERS}" >> "$GITHUB_OUTPUT" + else + echo "⚠️ revision 差异对比失败,保守回退为全量重建" + ALL_DRIVERS="$(bash ./tools/detect-changed-driver-agents.sh --base all --head "$GITHUB_SHA")" + echo "drivers=${ALL_DRIVERS}" >> "$GITHUB_OUTPUT" + fi + fi + + DRIVERS="$(sed -n 's/^drivers=//p' "$GITHUB_OUTPUT" | tail -n 1)" + if [[ -n "$DRIVERS" ]]; then + echo "has_changes=true" >> "$GITHUB_OUTPUT" + else + echo "has_changes=false" >> "$GITHUB_OUTPUT" + fi + + - name: Build Optional Driver Agents + if: ${{ matrix.build_optional_agents && steps.revision_diff.outputs.has_changes == 'true' }} + shell: bash env: - CHANGED_DRIVER_AGENTS: ${{ needs.driver_agents.outputs.drivers }} + CHANGED_DRIVER_AGENTS: ${{ steps.revision_diff.outputs.drivers }} run: | set -euo pipefail TARGET_PLATFORM="${{ matrix.platform }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 295b407..914234a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v6 with: - go-version: '1.24' + go-version: '1.24.3' - name: Setup Node uses: actions/setup-node@v5 @@ -66,6 +66,7 @@ jobs: drivers: ${{ steps.detect.outputs.drivers }} has_changes: ${{ steps.detect.outputs.has_changes }} release_source: ${{ steps.detect.outputs.release_source }} + compare_base: ${{ steps.detect.outputs.compare_base }} steps: - name: Checkout code uses: actions/checkout@v5 @@ -77,6 +78,31 @@ jobs: shell: bash run: | set -euo pipefail + merge_csv() { + local current="$1" + local incoming="$2" + local merged="$current" + local seen=",${current}," + local item + IFS=',' read -r -a items <<< "$incoming" + for item in "${items[@]}"; do + item="$(printf '%s' "$item" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" + if [[ -z "$item" ]]; then + continue + fi + if [[ "$seen" == *",$item,"* ]]; then + continue + fi + if [[ -n "$merged" ]]; then + merged="${merged},${item}" + else + merged="$item" + fi + seen="${seen}${item}," + done + printf '%s\n' "$merged" + } + git fetch --force --tags PREV_TAG="$(git describe --tags --match 'v*' --abbrev=0 "${GITHUB_SHA}^" 2>/dev/null || true)" if [ -n "$PREV_TAG" ]; then @@ -87,8 +113,25 @@ jobs: RELEASE_SOURCE="all" fi DRIVERS="$(bash ./tools/detect-changed-driver-agents.sh --base "$BASE_REF" --head "$GITHUB_SHA")" + if [[ "$BASE_REF" != "all" ]]; then + REVISION_DRIVERS="" + for PLATFORM in darwin/amd64 darwin/arm64 windows/amd64 windows/arm64 linux/amd64; do + PLATFORM_DRIVERS="$(bash ./tools/diff-driver-agent-revisions.sh --base "$BASE_REF" --head "$GITHUB_SHA" --platform "$PLATFORM")" || { + echo "⚠️ 平台 revision 差异对比失败(${PLATFORM}),保守回退为全量驱动重建" + DRIVERS="$(bash ./tools/detect-changed-driver-agents.sh --base all --head "$GITHUB_SHA")" + REVISION_DRIVERS="" + break + } + REVISION_DRIVERS="$(merge_csv "$REVISION_DRIVERS" "$PLATFORM_DRIVERS")" + done + if [[ -n "$REVISION_DRIVERS" ]]; then + echo "🧭 Revision diff union drivers: $REVISION_DRIVERS" + DRIVERS="$(merge_csv "$DRIVERS" "$REVISION_DRIVERS")" + fi + fi echo "drivers=${DRIVERS}" >> "$GITHUB_OUTPUT" echo "release_source=${RELEASE_SOURCE}" >> "$GITHUB_OUTPUT" + echo "compare_base=${BASE_REF}" >> "$GITHUB_OUTPUT" if [ -n "$DRIVERS" ]; then echo "has_changes=true" >> "$GITHUB_OUTPUT" echo "🧭 Changed driver agents since ${BASE_REF}: $DRIVERS" @@ -167,11 +210,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v5 + with: + fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v6 with: - go-version: '1.24' + go-version: '1.24.3' - name: Download frontend dist uses: actions/download-artifact@v7 @@ -341,11 +386,42 @@ jobs: wails build -s -skipbindings -platform ${{ matrix.platform }} -clean -o ${{ matrix.build_name }} -ldflags "-s -w -X GoNavi-Wails/internal/app.AppVersion=${{ github.ref_name }}" fi - - name: Build Optional Driver Agents + - name: Resolve Platform Driver Revision Diff + id: revision_diff if: ${{ matrix.build_optional_agents && needs.driver_agents.outputs.has_changes == 'true' }} shell: bash + run: | + set -euo pipefail + BASE_REF="${{ needs.driver_agents.outputs.compare_base }}" + FALLBACK_DRIVERS="${{ needs.driver_agents.outputs.drivers }}" + + if [[ -z "$BASE_REF" || "$BASE_REF" == "all" ]]; then + echo "⚠️ 未拿到可比对的历史 release 基线,回退使用全局检测结果:${FALLBACK_DRIVERS}" + echo "drivers=${FALLBACK_DRIVERS}" >> "$GITHUB_OUTPUT" + else + echo "🧭 对比当前平台 revision:base=${BASE_REF} head=${GITHUB_SHA} platform=${{ matrix.platform }}" + if DRIVERS="$(bash ./tools/diff-driver-agent-revisions.sh --base "$BASE_REF" --head "$GITHUB_SHA" --platform "${{ matrix.platform }}")"; then + echo "🧭 当前平台实际需要重建的 driver agents: ${DRIVERS:-}" + echo "drivers=${DRIVERS}" >> "$GITHUB_OUTPUT" + else + echo "⚠️ revision 差异对比失败,保守回退为全量重建" + ALL_DRIVERS="$(bash ./tools/detect-changed-driver-agents.sh --base all --head "$GITHUB_SHA")" + echo "drivers=${ALL_DRIVERS}" >> "$GITHUB_OUTPUT" + fi + fi + + DRIVERS="$(sed -n 's/^drivers=//p' "$GITHUB_OUTPUT" | tail -n 1)" + if [[ -n "$DRIVERS" ]]; then + echo "has_changes=true" >> "$GITHUB_OUTPUT" + else + echo "has_changes=false" >> "$GITHUB_OUTPUT" + fi + + - name: Build Optional Driver Agents + if: ${{ matrix.build_optional_agents && steps.revision_diff.outputs.has_changes == 'true' }} + shell: bash env: - CHANGED_DRIVER_AGENTS: ${{ needs.driver_agents.outputs.drivers }} + CHANGED_DRIVER_AGENTS: ${{ steps.revision_diff.outputs.drivers }} run: | set -euo pipefail TARGET_PLATFORM="${{ matrix.platform }}" diff --git a/tools/diff-driver-agent-revisions.sh b/tools/diff-driver-agent-revisions.sh new file mode 100644 index 0000000..c88959d --- /dev/null +++ b/tools/diff-driver-agent-revisions.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$SCRIPT_DIR" + +DEFAULT_DRIVERS=(mariadb oceanbase diros starrocks sphinx sqlserver sqlite duckdb dameng kingbase highgo vastbase opengauss iris mongodb tdengine clickhouse elasticsearch) + +usage() { + cat <<'EOF' +用法: + ./tools/diff-driver-agent-revisions.sh --base --head --platform + +输出: + 逗号分隔的 driver-agent 列表;当 base/head 在当前 runner + 指定平台上生成出的 revision 完全一致时输出空行。 + +说明: + 该脚本会分别在 base/head 对应源码上重算指定平台的 driver-agent revision, + 并按实际 revision 差异判定哪些驱动必须重建。 +EOF +} + +join_drivers() { + local IFS=, + echo "$*" +} + +public_driver_name() { + case "$1" in + diros) echo "doris" ;; + *) echo "$1" ;; + esac +} + +extract_revision() { + local file="$1" + local driver="$2" + awk -v target="$driver" ' + $0 ~ "\"" target "\"" { + if (match($0, /"src-[^"]+"/)) { + print substr($0, RSTART + 1, RLENGTH - 2) + exit + } + } + ' "$file" +} + +base_ref="" +head_ref="" +target_platform="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --base) + base_ref="${2:-}" + shift 2 + ;; + --head) + head_ref="${2:-}" + shift 2 + ;; + --platform) + target_platform="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "未知参数:$1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "$base_ref" || -z "$head_ref" || -z "$target_platform" ]]; then + usage >&2 + exit 1 +fi +if [[ "$target_platform" != */* ]]; then + echo "--platform 参数格式错误,应为 GOOS/GOARCH,例如 darwin/arm64" >&2 + exit 1 +fi + +if ! git rev-parse --verify "${base_ref}^{commit}" >/dev/null 2>&1; then + echo "无法解析 base ref:$base_ref" >&2 + exit 1 +fi +if ! git rev-parse --verify "${head_ref}^{commit}" >/dev/null 2>&1; then + echo "无法解析 head ref:$head_ref" >&2 + exit 1 +fi + +base_commit="$(git rev-parse "${base_ref}^{commit}")" +head_commit="$(git rev-parse "${head_ref}^{commit}")" + +if [[ "$base_commit" == "$head_commit" ]]; then + echo "" + exit 0 +fi + +base_worktree="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-driver-rev-base.XXXXXX")" +head_worktree="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-driver-rev-head.XXXXXX")" + +cleanup() { + git worktree remove --force "$base_worktree" >/dev/null 2>&1 || true + git worktree remove --force "$head_worktree" >/dev/null 2>&1 || true + rm -rf "$base_worktree" "$head_worktree" +} +trap cleanup EXIT + +git worktree add --detach "$base_worktree" "$base_commit" >/dev/null +git worktree add --detach "$head_worktree" "$head_commit" >/dev/null + +generate_revisions() { + local worktree="$1" + ( + cd "$worktree" + GONAVI_DRIVER_REVISION_JOBS="${GONAVI_DRIVER_REVISION_JOBS:-1}" \ + bash ./tools/generate-driver-agent-revisions.sh --platform "$target_platform" >/dev/null + ) +} + +generate_revisions "$base_worktree" +generate_revisions "$head_worktree" + +base_file="$base_worktree/internal/db/driver_agent_revisions_gen.go" +head_file="$head_worktree/internal/db/driver_agent_revisions_gen.go" + +declare -a changed_drivers=() +for driver in "${DEFAULT_DRIVERS[@]}"; do + base_revision="$(extract_revision "$base_file" "$driver")" + head_revision="$(extract_revision "$head_file" "$driver")" + if [[ "$base_revision" != "$head_revision" ]]; then + changed_drivers+=("$(public_driver_name "$driver")") + fi +done + +if [[ ${#changed_drivers[@]} -eq 0 ]]; then + echo "" +else + join_drivers "${changed_drivers[@]}" +fi diff --git a/tools/diff-driver-agent-revisions.test.sh b/tools/diff-driver-agent-revisions.test.sh new file mode 100644 index 0000000..8c90285 --- /dev/null +++ b/tools/diff-driver-agent-revisions.test.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$SCRIPT_DIR" + +same_commit_result="$(bash ./tools/diff-driver-agent-revisions.sh --base HEAD --head HEAD --platform darwin/arm64)" +if [[ -n "$same_commit_result" ]]; then + echo "expected same commit revision diff to be empty, got: ${same_commit_result}" >&2 + exit 1 +fi + +tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-diff-driver-revisions.XXXXXX")" +cleanup() { + rm -rf "$tmpdir" +} +trap cleanup EXIT + +rsync -a --exclude .git ./ "$tmpdir/" >/dev/null + +( + cd "$tmpdir" + git init -q + git add . + git -c user.name=GoNavi -c user.email=gonavi@example.test commit -q -m initial + base="$(git rev-parse HEAD)" + + perl -0pi -e 's/type DuckDB struct \{/\/\/ test revision change\n&type DuckDB struct {/' internal/db/duckdb_impl.go + git add internal/db/duckdb_impl.go + git -c user.name=GoNavi -c user.email=gonavi@example.test commit -q -m 'touch duckdb impl' + + actual="$(bash ./tools/diff-driver-agent-revisions.sh --base "$base" --head HEAD --platform darwin/arm64)" + if [[ "$actual" != *"duckdb"* ]]; then + echo "expected duckdb-specific source change to include duckdb revision rebuild, got: ${actual:-}" >&2 + exit 1 + fi +) + +tmpdir_frontend="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-diff-driver-revisions-frontend.XXXXXX")" +cleanup_frontend() { + rm -rf "$tmpdir_frontend" +} +trap cleanup_frontend EXIT + +rsync -a --exclude .git ./ "$tmpdir_frontend/" >/dev/null + +( + cd "$tmpdir_frontend" + git init -q + git add . + git -c user.name=GoNavi -c user.email=gonavi@example.test commit -q -m initial + base="$(git rev-parse HEAD)" + + perl -0pi -e 's/isAddingPrimaryKey:/isAddingPrimaryKeyFlag:/' frontend/src/components/tableDesignerDuckDbPrimaryKey.ts + git add frontend/src/components/tableDesignerDuckDbPrimaryKey.ts + git -c user.name=GoNavi -c user.email=gonavi@example.test commit -q -m 'touch frontend only' + + actual="$(bash ./tools/diff-driver-agent-revisions.sh --base "$base" --head HEAD --platform darwin/arm64)" + if [[ -n "$actual" ]]; then + echo "expected frontend-only change to keep driver revision diff empty, got: ${actual}" >&2 + exit 1 + fi +) + +echo "diff-driver-agent-revisions test passed"