mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-14 18:39:54 +08:00
🐛 fix(ci): 修复 dev CI 漏掉驱动补偿构建
- dev 构建改为优先使用已发布 driver release 对应源码提交作为检测基线 - 避免驱动相关提交的 CI 失败后,后续仅修 workflow 时被误判为无需重建驱动 - 当已发布驱动基线无法解析时,保守回退为全量驱动构建 - 补充驱动变更检测补偿场景回归测试 - 新增 driver release source 解析测试,覆盖 release body 与 target_commitish 场景
This commit is contained in:
32
.github/workflows/dev-build.yml
vendored
32
.github/workflows/dev-build.yml
vendored
@@ -76,19 +76,41 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve published driver release source
|
||||
id: published_source
|
||||
env:
|
||||
DRIVER_RELEASE_TOKEN: ${{ secrets.DRIVER_RELEASE_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SOURCE_COMMIT="$(python3 tools/resolve-driver-release-source.py --repo Syngnat/GoNavi-DriverAgents --tag dev-latest)"
|
||||
echo "source_commit=${SOURCE_COMMIT}" >> "$GITHUB_OUTPUT"
|
||||
if [[ -n "$SOURCE_COMMIT" ]]; then
|
||||
echo "🧭 Last published dev driver release source commit: $SOURCE_COMMIT"
|
||||
else
|
||||
echo "🧭 Unable to resolve published dev driver release source commit; fallback to push diff base"
|
||||
fi
|
||||
|
||||
- name: Detect changed driver agents
|
||||
id: detect
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
BASE_REF="${{ github.event.before }}"
|
||||
if [[ -z "$BASE_REF" || "$BASE_REF" =~ ^0+$ ]]; then
|
||||
if BASE_REF="$(git rev-parse HEAD^ 2>/dev/null)"; then
|
||||
:
|
||||
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
|
||||
echo "🧭 Using last published driver release source commit as detection base: $BASE_REF"
|
||||
else
|
||||
BASE_REF="all"
|
||||
echo "⚠️ Published driver release source commit is unavailable or not an ancestor of $GITHUB_SHA: $BASE_REF"
|
||||
BASE_REF=""
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$BASE_REF" ]]; then
|
||||
echo "⚠️ Falling back to full driver rebuild because published driver release source commit is unavailable"
|
||||
BASE_REF="all"
|
||||
fi
|
||||
echo "🧭 Final driver detection base: $BASE_REF"
|
||||
DRIVERS="$(bash ./tools/detect-changed-driver-agents.sh --base "$BASE_REF" --head "$GITHUB_SHA")"
|
||||
echo "drivers=${DRIVERS}" >> "$GITHUB_OUTPUT"
|
||||
if [ -n "$DRIVERS" ]; then
|
||||
|
||||
@@ -1 +1 @@
|
||||
d0464f9da25e9356e61652e638c99ffe
|
||||
0295a42fd931778d85157816d79d29e5
|
||||
@@ -69,4 +69,49 @@ cp tools/detect-changed-driver-agents.sh "$tmpdir_script/tools/detect-changed-dr
|
||||
fi
|
||||
)
|
||||
|
||||
tmpdir_compensation="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-detect-compensation.XXXXXX")"
|
||||
git init -q "$tmpdir_compensation"
|
||||
mkdir -p "$tmpdir_compensation/tools" "$tmpdir_compensation/internal/db" "$tmpdir_compensation/.github/workflows"
|
||||
cp tools/detect-changed-driver-agents.sh "$tmpdir_compensation/tools/detect-changed-driver-agents.sh"
|
||||
cat >"$tmpdir_compensation/internal/db/driver_agent_revisions_gen.go" <<'GOEOF'
|
||||
package db
|
||||
|
||||
func init() {
|
||||
optionalDriverAgentRevisions = map[string]string{
|
||||
"clickhouse": "src-old-clickhouse",
|
||||
}
|
||||
}
|
||||
GOEOF
|
||||
cat >"$tmpdir_compensation/.github/workflows/dev-build.yml" <<'YAMLEOF'
|
||||
name: Dev Build
|
||||
YAMLEOF
|
||||
|
||||
(
|
||||
cd "$tmpdir_compensation"
|
||||
git add .
|
||||
git -c user.name=GoNavi -c user.email=gonavi@example.test commit -q -m initial
|
||||
published_base="$(git rev-parse HEAD)"
|
||||
|
||||
perl -0pi -e 's/src-old-clickhouse/src-new-clickhouse/' internal/db/driver_agent_revisions_gen.go
|
||||
git add internal/db/driver_agent_revisions_gen.go
|
||||
git -c user.name=GoNavi -c user.email=gonavi@example.test commit -q -m 'update clickhouse revision'
|
||||
push_base="$(git rev-parse HEAD)"
|
||||
|
||||
printf '\n# workflow fix\n' >> .github/workflows/dev-build.yml
|
||||
git add .github/workflows/dev-build.yml
|
||||
git -c user.name=GoNavi -c user.email=gonavi@example.test commit -q -m 'fix workflow'
|
||||
|
||||
actual_push_base="$(bash ./tools/detect-changed-driver-agents.sh --base "$push_base" --head HEAD)"
|
||||
if [[ -n "$actual_push_base" ]]; then
|
||||
echo "expected workflow-only fix commit to be empty when using push diff base, got: ${actual_push_base:-<empty>}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
actual_published_base="$(bash ./tools/detect-changed-driver-agents.sh --base "$published_base" --head HEAD)"
|
||||
if [[ "$actual_published_base" != "clickhouse" ]]; then
|
||||
echo "expected published release base to recover clickhouse rebuild, got: ${actual_published_base:-<empty>}" >&2
|
||||
exit 1
|
||||
fi
|
||||
)
|
||||
|
||||
echo "detect-changed-driver-agents revision test passed"
|
||||
|
||||
103
tools/resolve-driver-release-source.py
Normal file
103
tools/resolve-driver-release-source.py
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
|
||||
COMMIT_LINK_RE = re.compile(r"/commit/([0-9a-f]{40})(?:\b|/)")
|
||||
FULL_SHA_RE = re.compile(r"\b([0-9a-f]{40})\b")
|
||||
|
||||
|
||||
def github_headers():
|
||||
headers = {
|
||||
"Accept": "application/vnd.github+json",
|
||||
"User-Agent": "GoNavi-CI",
|
||||
}
|
||||
token = os.environ.get("DRIVER_RELEASE_TOKEN") or os.environ.get("GITHUB_TOKEN")
|
||||
if token:
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
return headers
|
||||
|
||||
|
||||
def fetch_json(url):
|
||||
request = urllib.request.Request(url, headers=github_headers())
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
return json.loads(response.read().decode("utf-8"))
|
||||
|
||||
|
||||
def load_release(repo, tag):
|
||||
owner_repo = repo.strip()
|
||||
if not owner_repo:
|
||||
raise ValueError("repo is required")
|
||||
|
||||
if tag == "latest":
|
||||
url = f"https://api.github.com/repos/{owner_repo}/releases/latest"
|
||||
else:
|
||||
url = (
|
||||
f"https://api.github.com/repos/{owner_repo}/releases/tags/"
|
||||
f"{urllib.parse.quote(tag, safe='')}"
|
||||
)
|
||||
|
||||
try:
|
||||
return fetch_json(url)
|
||||
except urllib.error.HTTPError as exc:
|
||||
if exc.code == 404:
|
||||
print(f"warning: release {owner_repo}@{tag} not found", file=sys.stderr)
|
||||
return None
|
||||
print(
|
||||
f"warning: failed to load release {owner_repo}@{tag}: HTTP {exc.code}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return None
|
||||
except Exception as exc: # pragma: no cover - defensive logging path
|
||||
print(f"warning: failed to load release {owner_repo}@{tag}: {exc}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def extract_source_commit(release):
|
||||
if not isinstance(release, dict):
|
||||
return None
|
||||
|
||||
body = str(release.get("body") or "")
|
||||
for pattern in (COMMIT_LINK_RE, FULL_SHA_RE):
|
||||
match = pattern.search(body)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
target_commitish = str(release.get("target_commitish") or "").strip()
|
||||
if FULL_SHA_RE.fullmatch(target_commitish):
|
||||
return target_commitish
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--repo", default="Syngnat/GoNavi-DriverAgents")
|
||||
parser.add_argument("--tag", required=True, help="release tag name such as dev-latest or v1.0.0")
|
||||
args = parser.parse_args()
|
||||
|
||||
release = load_release(args.repo, args.tag)
|
||||
if release is None:
|
||||
return 0
|
||||
|
||||
source_commit = extract_source_commit(release)
|
||||
if not source_commit:
|
||||
print(
|
||||
f"warning: release {args.repo}@{args.tag} does not expose source commit",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 0
|
||||
|
||||
print(source_commit)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
42
tools/resolve-driver-release-source.test.py
Normal file
42
tools/resolve-driver-release-source.test.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import importlib.util
|
||||
import pathlib
|
||||
import unittest
|
||||
|
||||
|
||||
MODULE_PATH = pathlib.Path(__file__).with_name("resolve-driver-release-source.py")
|
||||
SPEC = importlib.util.spec_from_file_location("resolve_driver_release_source", MODULE_PATH)
|
||||
MODULE = importlib.util.module_from_spec(SPEC)
|
||||
assert SPEC.loader is not None
|
||||
SPEC.loader.exec_module(MODULE)
|
||||
|
||||
|
||||
class ResolveDriverReleaseSourceTests(unittest.TestCase):
|
||||
def test_extracts_commit_from_release_body_link(self):
|
||||
commit = "a" * 40
|
||||
release = {
|
||||
"body": (
|
||||
f"GoNavi dev driver-agent assets.\n\n"
|
||||
f"**提交**: [`{commit}`](https://github.com/Syngnat/GoNavi/commit/{commit})"
|
||||
)
|
||||
}
|
||||
self.assertEqual(MODULE.extract_source_commit(release), commit)
|
||||
|
||||
def test_extracts_commit_from_plain_body_sha(self):
|
||||
commit = "b" * 40
|
||||
release = {"body": f"source commit: {commit}"}
|
||||
self.assertEqual(MODULE.extract_source_commit(release), commit)
|
||||
|
||||
def test_falls_back_to_full_sha_target_commitish(self):
|
||||
commit = "c" * 40
|
||||
release = {"target_commitish": commit}
|
||||
self.assertEqual(MODULE.extract_source_commit(release), commit)
|
||||
|
||||
def test_ignores_branch_name_target_commitish(self):
|
||||
release = {"body": "", "target_commitish": "main"}
|
||||
self.assertIsNone(MODULE.extract_source_commit(release))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user