已合并
【fix】修复pre-commit的oat无告警报错 #3826
zhangfands创建于 7月6日
【fix】修复pre-commit的oat无告警报错 #3826
已合并
zhangfands创建于 7月6日
1 个文件变更+164-27
@@ -1,10 +1,10 @@
1#!/bin/sh1#!/bin/sh
2# -----------------------------------------------------------------------------------------------------------2# -----------------------------------------------------------------------------------------------------------
3# Copyright (c) 2026 Huawei Technologies Co., Ltd.3# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4-# This program is free software, you can redistribute it and/or modify it under the terms and conditions of 4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5# CANN Open Software License Agreement Version 2.0 (the "License").5# CANN Open Software License Agreement Version 2.0 (the "License").
6# Please refer to the License for details. You may not use this file except in compliance with the License.6# Please refer to the License for details. You may not use this file except in compliance with the License.
7-# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, 7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.8# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9# See LICENSE in the root of the software repository for the full text of the License.9# See LICENSE in the root of the software repository for the full text of the License.
10# -----------------------------------------------------------------------------------------------------------10# -----------------------------------------------------------------------------------------------------------
@@ -48,12 +48,25 @@ if [ -z "$_PYTHON" ]; then
48fi48fi
49 49 
50# ---------------------------------------------------------------------------50# ---------------------------------------------------------------------------
51-# 1. Ensure oat-py is installed51+# 1. Ensure oat-py is installed (serialized via flock to prevent parallel pip conflicts)
52# ---------------------------------------------------------------------------52# ---------------------------------------------------------------------------
53_OAT_OK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")53_OAT_OK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")
54if [ "$_OAT_OK" != "ok" ]; then54if [ "$_OAT_OK" != "ok" ]; then
55 echo "[OAT] oat-py not found. Installing oat-py>=1.0.1 ..."55 echo "[OAT] oat-py not found. Installing oat-py>=1.0.1 ..."
56- "$_PYTHON" -m pip install --quiet "oat-py>=1.0.1"56+ _LOCK_FILE="/tmp/oat_pip_install.lock"
57+ # flock ensures only one concurrent invocation runs pip install at a time;
58+ # re-check inside the lock so processes that waited skip redundant installs
59+ if command -v flock >/dev/null 2>&1; then
60+ (
61+ flock -w 120 9
62+ _RECHECK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")
63+ if [ "$_RECHECK" != "ok" ]; then
64+ "$_PYTHON" -m pip install --quiet "oat-py>=1.0.1"
65+ fi
66+ ) 9>"$_LOCK_FILE"
67+ else
68+ "$_PYTHON" -m pip install --quiet "oat-py>=1.0.1"
69+ fi
57 _OAT_OK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")70 _OAT_OK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")
58 if [ "$_OAT_OK" != "ok" ]; then71 if [ "$_OAT_OK" != "ok" ]; then
59 echo "[OAT] [WARNING] Failed to install oat-py. Please run: pip install oat-py>=1.0.1"72 echo "[OAT] [WARNING] Failed to install oat-py. Please run: pip install oat-py>=1.0.1"
@@ -68,10 +81,99 @@ fi
68# ---------------------------------------------------------------------------81# ---------------------------------------------------------------------------
69REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)82REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
70REPO_NAME=$(basename "$REPO_ROOT")83REPO_NAME=$(basename "$REPO_ROOT")
71-OAT_REPORT_DIR="$REPO_ROOT/oat_reports"84+OAT_REPORT_DIR="${TMPDIR:-/tmp}/oat_reports_$$"
85+OAT_RESULT_DIR="$REPO_ROOT/oat_reports"
72 86 
73-echo "[OAT] Running OAT scan (Python Edition) — INCREMENTAL MODE"87+# ---------------------------------------------------------------------------
88+# 2a. PR-range deduplication (pure git, no CI env vars required)
89+#
90+# Strategy:
91+# 1. Find the merge-base between HEAD and the upstream branch (origin/master
92+# or similar). If found, this is a feature-branch context ??collect ALL
93+# files changed since the branch diverged (full PR diff).
94+# 2. Key a done-marker on the HEAD SHA. The first invocation runs the scan;
95+# every subsequent invocation for the same HEAD exits 0 immediately.
96+# This eliminates redundant scans when CI calls the hook once per commit.
97+# 3. If no merge-base is found (e.g. committing directly on master) fall back
98+# to scanning only the currently staged files, with no done-marker.
99+# ---------------------------------------------------------------------------
100+_PR_MERGE_BASE=""
101+_DONE_MARKER=""
102+_HEAD_SHA=$(git rev-parse HEAD 2>/dev/null | cut -c1-12 || true)
103+ 
104+if [ -n "$_HEAD_SHA" ]; then
105+ # Try to find merge-base with a known upstream branch
106+ if [ -n "${PRE_COMMIT_FROM_REF:-}" ]; then
107+ # pre-commit --from-ref/--to-ref: use the provided base directly
108+ _PR_MERGE_BASE=$(git merge-base "$PRE_COMMIT_FROM_REF" HEAD 2>/dev/null || true)
109+ fi
110+ if [ -z "$_PR_MERGE_BASE" ]; then
111+ # Search all remotes (origin, upstream, etc.) for common trunk branch names.
112+ # This handles fork setups where origin=fork and upstream=main-repo.
113+ _CANDIDATE_BASE=""
114+ _CANDIDATE_DIST=0
115+ for _b in $(git for-each-ref --format='%(refname:short)' \
116+ 'refs/remotes/*/master' 'refs/remotes/*/main' \
117+ 'refs/remotes/*/develop' 'refs/remotes/*/dev' 2>/dev/null); do
118+ _mb=$(git merge-base HEAD "$_b" 2>/dev/null || true)
119+ [ -z "$_mb" ] && continue
120+ # Skip if merge-base is HEAD itself (branch is ahead of or equal to HEAD)
121+ [ "$_mb" = "$(git rev-parse HEAD 2>/dev/null)" ] && continue
122+ # Pick the candidate whose merge-base is furthest from HEAD
123+ # (most commits since divergence = most likely true PR base)
124+ _dist=$(git rev-list --count "$_mb"..HEAD 2>/dev/null || echo 0)
125+ if [ -z "$_CANDIDATE_BASE" ] || [ "$_dist" -gt "$_CANDIDATE_DIST" ]; then
126+ _CANDIDATE_BASE="$_mb"
127+ _CANDIDATE_DIST="$_dist"
128+ fi
129+ done
130+ _PR_MERGE_BASE="$_CANDIDATE_BASE"
131+ fi
132+ 
133+ # Only use PR-range mode when HEAD has diverged from the upstream base
134+ if [ -n "$_PR_MERGE_BASE" ] && [ "$_PR_MERGE_BASE" != "$(git rev-parse HEAD 2>/dev/null)" ]; then
135+ mkdir -p "$OAT_RESULT_DIR"
136+ _DONE_MARKER="$OAT_RESULT_DIR/.done_${_HEAD_SHA}"
137+ if [ -f "$_DONE_MARKER" ]; then
138+ echo "[OAT] [SKIP] Already scanned HEAD=${_HEAD_SHA}. Skipping duplicate invocation."
139+ exit 0
140+ fi
141+ else
142+ # HEAD == merge-base: on the upstream branch itself, no PR range
143+ _PR_MERGE_BASE=""
144+ fi
145+fi
146+ 
147+# ---------------------------------------------------------------------------
148+# 2b. Deduplicate parallel invocations within the same pre-commit run.
149+# Only the first instance that acquires the lock actually runs the scan;
150+# all others wait then exit 0.
151+# ---------------------------------------------------------------------------
152+if command -v flock >/dev/null 2>&1; then
153+ _OAT_SCAN_LOCK="${TMPDIR:-/tmp}/oat_scan_$(echo "$REPO_ROOT" | tr '/\\: ' '____').lock"
154+ exec 9>"$_OAT_SCAN_LOCK"
155+ if ! flock -n 9; then
156+ echo "[OAT] Another OAT scan instance is running. Waiting..."
157+ flock -w 120 9
158+ # Re-check done marker: if the scanning instance completed normally, skip.
159+ # If it exited abnormally (no marker), fall through and run the scan ourselves.
160+ if [ -n "$_DONE_MARKER" ] && [ -f "$_DONE_MARKER" ]; then
161+ echo "[OAT] Scan already completed by another instance. Skipping."
162+ exit 0
163+ fi
164+ echo "[OAT] Previous instance did not complete. Proceeding with scan..."
165+ # Fall through to run the scan below
166+ fi
167+ # This instance now owns the lock and will run the scan.
168+fi
169+ 
170+echo "[OAT] Running OAT scan (Python Edition) ??INCREMENTAL MODE"
74echo "[OAT] Project: $REPO_NAME"171echo "[OAT] Project: $REPO_NAME"
172+if [ -n "$_PR_MERGE_BASE" ]; then
173+ echo "[OAT] Mode: PR range ??scanning all files changed since merge-base"
174+else
175+ echo "[OAT] Mode: staged files ??scanning only currently staged files"
176+fi
75 177 
76# ---------------------------------------------------------------------------178# ---------------------------------------------------------------------------
77# 3. Collect staged files179# 3. Collect staged files
@@ -93,7 +195,31 @@ if [ $# -gt 0 ]; then
93 FILE_LIST="$FILE_LIST,$_abs"195 FILE_LIST="$FILE_LIST,$_abs"
94 fi196 fi
95 done197 done
198+elif [ -n "$_PR_MERGE_BASE" ]; then
199+ # PR range mode: collect ALL files changed since the branch diverged
200+ _STAGED=$(git diff --name-only --diff-filter=ACM "$_PR_MERGE_BASE" HEAD \
201+ 2>/dev/null || true)
202+ if [ -z "$_STAGED" ]; then
203+ echo "[OAT] No files changed in PR range. Skipping."
204+ [ -n "$_DONE_MARKER" ] && touch "$_DONE_MARKER" 2>/dev/null || true
205+ exit 0
206+ fi
207+ FILE_COUNT=$(echo "$_STAGED" | wc -l | tr -d ' ')
208+ FILE_LIST=""
209+ for _f in $_STAGED; do
210+ case "$_f" in
211+ /*) _abs="$_f" ;;
212+ *) _abs="$REPO_ROOT/$_f" ;;
213+ esac
214+ [ -f "$_abs" ] || continue
215+ if [ -z "$FILE_LIST" ]; then
216+ FILE_LIST="$_abs"
217+ else
218+ FILE_LIST="$FILE_LIST,$_abs"
219+ fi
220+ done
96else221else
222+ # Staged files mode: on upstream branch directly, scan only staged files
97 _STAGED=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || true)223 _STAGED=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || true)
98 if [ -z "$_STAGED" ]; then224 if [ -z "$_STAGED" ]; then
99 echo "[OAT] No staged files to check. Skipping."225 echo "[OAT] No staged files to check. Skipping."
@@ -123,20 +249,13 @@ fi
123echo "[OAT] Checking $FILE_COUNT staged file(s)..."249echo "[OAT] Checking $FILE_COUNT staged file(s)..."
124 250 
125# ---------------------------------------------------------------------------251# ---------------------------------------------------------------------------
126-# 4. Ensure oat_reports/ exists and is in .gitignore252+# 4. Ensure report directories exist
127# ---------------------------------------------------------------------------253# ---------------------------------------------------------------------------
128mkdir -p "$OAT_REPORT_DIR"254mkdir -p "$OAT_REPORT_DIR"
129- 255+mkdir -p "$OAT_RESULT_DIR"
130-_GITIGNORE="$REPO_ROOT/.gitignore"
131-for _entry in "oat_reports" "log"; do
132- if ! grep -qE "^${_entry}/?$" "$_GITIGNORE" 2>/dev/null; then
133- printf "\n%s/\n" "$_entry" >> "$_GITIGNORE" 2>/dev/null || true
134- echo "[OAT] Added ${_entry}/ to .gitignore"
135- fi
136-done
137 256 
138# ---------------------------------------------------------------------------257# ---------------------------------------------------------------------------
139-# 5. Build oat command use OAT.xml if present in repo root258+# 5. Build oat command ??use OAT.xml if present in repo root
140# ---------------------------------------------------------------------------259# ---------------------------------------------------------------------------
141_OAT_CMD="$_PYTHON -m oat -mode s -s $REPO_ROOT -r $OAT_REPORT_DIR -n $REPO_NAME -w 1 -f $FILE_LIST"260_OAT_CMD="$_PYTHON -m oat -mode s -s $REPO_ROOT -r $OAT_REPORT_DIR -n $REPO_NAME -w 1 -f $FILE_LIST"
142 261 
@@ -170,7 +289,7 @@ fi
170# Only: Invalid File Type + License Header Invalid (no copyright)289# Only: Invalid File Type + License Header Invalid (no copyright)
171# ---------------------------------------------------------------------------290# ---------------------------------------------------------------------------
172REPORT_FILE="$OAT_REPORT_DIR/PlainReport_${REPO_NAME}.txt"291REPORT_FILE="$OAT_REPORT_DIR/PlainReport_${REPO_NAME}.txt"
173-RESULT_FILE="$OAT_REPORT_DIR/result.txt"292+RESULT_FILE="$OAT_RESULT_DIR/result.txt"
174 293 
175# Section headers used as stop-boundaries when extracting sections294# Section headers used as stop-boundaries when extracting sections
176_ALL_HEADERS="Invalid File Type Total Count:|License Not Compatible Total Count:|License Header Invalid Total Count:|Copyright Header Invalid Total Count:|No License File Total Count:|No Readme.OpenSource Total Count:|No Readme Total Count:|Import Invalid Total Count:|Redundant License File Total Count:|Third Party Software Info Total Count:"295_ALL_HEADERS="Invalid File Type Total Count:|License Not Compatible Total Count:|License Header Invalid Total Count:|Copyright Header Invalid Total Count:|No License File Total Count:|No Readme.OpenSource Total Count:|No Readme Total Count:|Import Invalid Total Count:|Redundant License File Total Count:|Third Party Software Info Total Count:"
@@ -201,17 +320,29 @@ _extract_section() {
201 320 
202if [ ! -f "$REPORT_FILE" ]; then321if [ ! -f "$REPORT_FILE" ]; then
203 if [ "$_OAT_RC" -eq 0 ]; then322 if [ "$_OAT_RC" -eq 0 ]; then
323+ # oat exited cleanly with no report: all staged files were filtered out
204 echo "[OAT] [OK] All checks passed ($FILE_COUNT file(s) checked)."324 echo "[OAT] [OK] All checks passed ($FILE_COUNT file(s) checked)."
325+ [ -n "$_DONE_MARKER" ] && touch "$_DONE_MARKER" 2>/dev/null || true
326+ rm -rf "$OAT_REPORT_DIR"
205 exit 0327 exit 0
206 else328 else
207- echo "[OAT] [WARNING] Report not found: $REPORT_FILE"329+ # oat returned exit code 1 but produced no report — possible disk issue
330+ # or oat internal error. Do not silently pass; block the commit.
331+ echo ""
332+ echo "[OAT] [ERROR] oat exited with code $_OAT_RC but no report was generated."
333+ echo "[OAT] This may indicate a disk error or an oat internal bug."
334+ echo "[OAT] To investigate, run manually:"
335+ echo " $_OAT_CMD"
336+ echo "[OAT] Blocking commit to prevent silent compliance bypass."
337+ echo ""
338+ rm -rf "$OAT_REPORT_DIR"
208 exit 1339 exit 1
209 fi340 fi
210fi341fi
211 342 
212-# Parse counts343+# Parse counts ??use || true to prevent set -e from triggering if grep finds no match
213-_INVALID_TYPE=$(grep "^Invalid File Type Total Count:" "$REPORT_FILE" | grep -oE '[0-9]+' | head -1)344+_INVALID_TYPE=$(grep "^Invalid File Type Total Count:" "$REPORT_FILE" | grep -oE '[0-9]+' | head -1 || true)
214-_LICENSE_INVALID=$(grep "^License Header Invalid Total Count:" "$REPORT_FILE" | grep -oE '[0-9]+' | head -1)345+_LICENSE_INVALID=$(grep "^License Header Invalid Total Count:" "$REPORT_FILE" | grep -oE '[0-9]+' | head -1 || true)
215_INVALID_TYPE=${_INVALID_TYPE:-0}346_INVALID_TYPE=${_INVALID_TYPE:-0}
216_LICENSE_INVALID=${_LICENSE_INVALID:-0}347_LICENSE_INVALID=${_LICENSE_INVALID:-0}
217 348 
@@ -240,11 +371,8 @@ _SECTION_LIC=$(_extract_section "$REPORT_FILE" "License Header Invalid Total Cou
240 echo "==================================="371 echo "==================================="
241} > "$RESULT_FILE"372} > "$RESULT_FILE"
242 373 
243-# Clean up full plain report (keep only result.txt)
244-rm -f "$REPORT_FILE"
245- 
246# ---------------------------------------------------------------------------374# ---------------------------------------------------------------------------
247-# 8. Block commit if issues found375+# 8. Block commit if issues found; always clean up temp dir
248# ---------------------------------------------------------------------------376# ---------------------------------------------------------------------------
249TOTAL_ISSUES=$(( _INVALID_TYPE + _LICENSE_INVALID ))377TOTAL_ISSUES=$(( _INVALID_TYPE + _LICENSE_INVALID ))
250 378 
@@ -258,12 +386,15 @@ if [ "$TOTAL_ISSUES" -gt 0 ]; then
258 echo " - Invalid File Type: $_INVALID_TYPE"386 echo " - Invalid File Type: $_INVALID_TYPE"
259 echo " - License Header Invalid: $_LICENSE_INVALID"387 echo " - License Header Invalid: $_LICENSE_INVALID"
260 echo ""388 echo ""
261- echo "[OAT] Details:"389+ echo "[OAT] Details (also saved to: $RESULT_FILE):"
262- echo " cat $RESULT_FILE"390+ echo "---"
391+ cat "$RESULT_FILE"
392+ echo "---"
263 echo ""393 echo ""
264 echo "Fix the issues and recommit, or skip with:"394 echo "Fix the issues and recommit, or skip with:"
265 echo " git commit --no-verify"395 echo " git commit --no-verify"
266 echo ""396 echo ""
397+ rm -rf "$OAT_REPORT_DIR"
267 exit 1398 exit 1
268fi399fi
269 400 
@@ -271,4 +402,10 @@ echo ""
271echo "[OAT] [OK] All checks passed ($FILE_COUNT file(s) checked)."402echo "[OAT] [OK] All checks passed ($FILE_COUNT file(s) checked)."
272echo "[OAT] Summary: cat $RESULT_FILE"403echo "[OAT] Summary: cat $RESULT_FILE"
273echo ""404echo ""
405+# Mark scan as done for CI range mode (prevents redundant re-runs on same PR head)
406+if [ -n "$_DONE_MARKER" ]; then
407+ touch "$_DONE_MARKER" 2>/dev/null || true
408+ echo "[OAT] Done-marker created: $_DONE_MARKER"
409+fi
410+rm -rf "$OAT_REPORT_DIR"
274exit 0411exit 0