diff --git a/.github/workflows/cleanup-prerelease-tags.yml b/.github/workflows/cleanup-prerelease-tags.yml new file mode 100644 index 00000000..5de62e4d --- /dev/null +++ b/.github/workflows/cleanup-prerelease-tags.yml @@ -0,0 +1,96 @@ +name: Cleanup prerelease tags + +on: + workflow_dispatch: + schedule: + - cron: '0 3 * * *' + +permissions: + contents: write + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve version metadata + id: version + run: | + SHOULD_RUN=true + VERSION="all-prerelease-tags" + + echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + if [[ "$VERSION" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then + echo "is_prerelease=false" >> "$GITHUB_OUTPUT" + else + echo "is_prerelease=true" >> "$GITHUB_OUTPUT" + fi + + - name: Delete prerelease, dangling, and unbound releases/tags + if: steps.version.outputs.should_run == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Fetch all tags from remote to ensure full synchronization + git fetch --tags --force + + # Get all local/remote git tags starting with 'v' + mapfile -t GIT_TAGS < <(git tag --list 'v*' | sort -V) + + # Get all GitHub releases (tags associated with releases) + mapfile -t GH_RELEASES < <(gh release list --limit 1000 --json tagName --jq '.[].tagName' 2>/dev/null || true) + + # Helper function to check array containment + contains_element() { + local e match="$1" + shift + for e; do [[ "$e" == "$match" ]] && return 0; done + return 1 + } + + DELETED_TAGS=0 + DELETED_RELEASES=0 + + echo "=== Phase 1: Checking and cleaning Git tags ===" + for TAG in "${GIT_TAGS[@]}"; do + if [[ "$TAG" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then + # Formal release tag + if ! contains_element "$TAG" "${GH_RELEASES[@]}"; then + echo "Delete formal tag not bound to any GitHub release: $TAG" + git push origin --delete "refs/tags/$TAG" || true + git tag -d "$TAG" || true + DELETED_TAGS=$((DELETED_TAGS + 1)) + else + echo "Keep formal release tag (bound to release): $TAG" + fi + else + # Prerelease tag + if contains_element "$TAG" "${GH_RELEASES[@]}"; then + echo "Delete prerelease release: $TAG" + gh release delete "$TAG" --yes || true + DELETED_RELEASES=$((DELETED_RELEASES + 1)) + fi + + echo "Delete prerelease tag: $TAG" + git push origin --delete "refs/tags/$TAG" || true + git tag -d "$TAG" || true + DELETED_TAGS=$((DELETED_TAGS + 1)) + fi + done + + echo "=== Phase 2: Checking and cleaning dangling GitHub releases ===" + for REL_TAG in "${GH_RELEASES[@]}"; do + if ! contains_element "$REL_TAG" "${GIT_TAGS[@]}"; then + echo "Delete GitHub release not bound to any Git tag: $REL_TAG" + gh release delete "$REL_TAG" --yes || true + DELETED_RELEASES=$((DELETED_RELEASES + 1)) + fi + done + + echo "=== Summary ===" + echo "Successfully deleted $DELETED_TAGS tag(s) and $DELETED_RELEASES release(s)." diff --git a/Agents.md b/Agents.md index 8a63e862..a74c1a1f 100644 --- a/Agents.md +++ b/Agents.md @@ -545,6 +545,9 @@ apps/admin// ## 代码规范 +> [!IMPORTANT] +> **代码开发完成后,必须在项目根目录运行 `make code-check` 进行本地代码质量与风格检查,确保前端 ESLint 没有报错和警告(`--max-warnings 0`)。** + ### 后端 **基础检查** diff --git a/Makefile b/Makefile index cdcb91d9..48ffbda2 100644 --- a/Makefile +++ b/Makefile @@ -15,5 +15,5 @@ build-embedded: cp -R frontend/out internal/router/dist go build -tags embed_frontend -o bin/wavelet main.go -pre-commit: +code-check: cd frontend && npx eslint . --max-warnings 0 diff --git a/frontend/components/common/admin/access-analytics.tsx b/frontend/components/common/admin/access-analytics.tsx index 6df958eb..8421ebfa 100644 --- a/frontend/components/common/admin/access-analytics.tsx +++ b/frontend/components/common/admin/access-analytics.tsx @@ -89,12 +89,13 @@ export function AccessAnalytics() { setBrowsers(data.browsers || []) setTopUsers(data.top_users || []) setClickhouseDisabled(false) - } catch (err: any) { - const errMsg = err?.message || "" + } catch (err) { + const errorInstance = err instanceof Error ? err : new Error("获取数据统计失败") + const errMsg = errorInstance.message || "" if (errMsg.includes("ClickHouse") || errMsg.includes("未启用")) { setClickhouseDisabled(true) } else { - setError(err instanceof Error ? err : new Error("获取数据统计失败")) + setError(errorInstance) } } finally { setLoading(false) @@ -287,7 +288,7 @@ export function AccessAnalytics() { ) : (
- {topUsers.map((user, index) => ( + {topUsers.map((user) => (
diff --git a/frontend/components/common/admin/access-logs.tsx b/frontend/components/common/admin/access-logs.tsx index 48d960fd..2341e909 100644 --- a/frontend/components/common/admin/access-logs.tsx +++ b/frontend/components/common/admin/access-logs.tsx @@ -120,12 +120,13 @@ export function AccessLogs() { setLogs(data.list || []) setTotal(data.total || 0) setClickhouseDisabled(false) - } catch (err: any) { - const errMsg = err?.message || "" + } catch (err) { + const errorInstance = err instanceof Error ? err : new Error("获取访问日志失败") + const errMsg = errorInstance.message || "" if (errMsg.includes("ClickHouse") || errMsg.includes("未启用")) { setClickhouseDisabled(true) } else { - setError(err instanceof Error ? err : new Error("获取访问日志失败")) + setError(errorInstance) } } finally { setLoading(false)