Skip to content

修正 OpenClaw 子开关可用性判断 (#1213) #2050

修正 OpenClaw 子开关可用性判断 (#1213)

修正 OpenClaw 子开关可用性判断 (#1213) #2050

Workflow file for this run

name: Analyze
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
# Static-check job only needs to read the checked-out source.
permissions:
contents: read
jobs:
python-lint:
name: Python lint (ruff + async-blocking + no-loguru + no-temperature + prompt-hygiene + i18n-sync + docs-no-relative-paths + api-trailing-slash + frontend-api-trailing-slash)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
# i18n-sync needs full history to diff against the merge-base of
# origin/main. Other lint steps don't care, but fetch-depth: 0 has
# negligible cost on this repo.
fetch-depth: 0
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install ruff
# ruff is the only dev dep we need for the lint job; a full uv sync
# pulls heavy native extensions (playwright, numpy, etc.) that are
# unnecessary for static checks.
run: uv tool install ruff==0.15.4
- name: ruff check (incl. ASYNC210/220/221/222/251)
run: ruff check .
- name: Forbid blocking calls in async def bodies
# Custom AST checker, broader than ruff's ASYNC* rules:
# * gaps flake8-async doesn't catch: Thread/Process.join,
# queue.Queue.get, raw socket recv/accept/connect
# * extra blocking stdlib/3p calls: PIL.Image.open,
# pyautogui.screenshot, Fernet encrypt/decrypt, shutil.*,
# json.load, plus bare-import forms (sleep, rmtree, urlopen)
# * depth-1 transitive: a sync helper whose body hits any of the
# above, called directly from async, is also flagged.
run: python scripts/check_async_blocking.py
- name: Forbid loguru / structlog / logbook imports
# Logging is unified through utils.logger_config (RobustLoggerConfig).
# Re-introducing a third-party logging frontend fragments the surface
# (formatter, sinks, file naming, multi-process behaviour) and breaks
# plugin/main parity. This check fails the build on any such import.
run: python scripts/check_no_loguru.py
- name: Forbid `temperature=` kwargs on LLM client calls (memory + utils)
# Project policy: do NOT pass `temperature=` to create_chat_llm /
# ChatOpenAI / wrappers in memory_server.py + memory/ + utils/. The
# default (None) omits the field — required for o1/o3/gpt-5-thinking/
# Claude extended-thinking, and avoids per-call-site temperature drift
# across memory tasks. See memory/__init__.py and .agent/rules/
# neko-guide.md for the rationale.
run: python scripts/check_no_temperature.py
- name: Enforce prompt-i18n conventions (inline-EN-only + multilang-in-config)
# Two rules in one walker:
# - INLINE_PROMPT_NON_EN: any string at an LLM call site or in a
# module-level *PROMPT/*INSTRUCTION/*SYSTEM constant must be
# English-bodied (CJK ratio <30%). Embedded short examples in
# CJK are allowed under the threshold.
# - I18N_NOT_IN_CONFIG: multi-language dicts (≥2 lang keys, must
# include 'en') belong in config/prompts_*.py, not regular code.
# Per-line `# noqa: <CODE>` suppression supported. See PR #974 for
# the reference incident that motivated this lint.
run: python scripts/check_prompt_hygiene.py
- name: Verify i18n locale files move in lockstep (PR-only)
# Diff-based: when ANY static/locales/*.json or
# frontend/plugin-manager/src/i18n/locales/*.ts changes, ALL files
# in the group must change AND every hunk must occupy the same line
# ranges across all languages. Skipped on direct push to main —
# there's nothing to diff against.
if: github.event_name == 'pull_request'
run: python scripts/check_i18n_sync.py --base "origin/${{ github.base_ref }}"
- name: Forbid relative-up markdown links inside docs/
# docs/ ships through VitePress with itself as the deploy root;
# any markdown link target starting with '..' resolves outside the
# site and breaks deploy. We've shipped this regression more than
# once — this check fails the build before the next attempt lands.
# Fix is to inline the path as code (`foo/bar.js`) instead of a
# link, or move the referenced content into docs/.
run: python scripts/check_docs_no_relative_paths.py
- name: Forbid trailing-slash route paths on FastAPI decorators (backend)
# Project convention: every backend HTTP/WebSocket endpoint is
# declared WITHOUT a trailing slash. Avoids Starlette's absolute-URL
# 307 redirect under reverse proxies — root cause of the PR #938
# chara_manager regression: nginx/etc that don't transparently
# forward Host send the redirect Location to 127.0.0.1:<internal>,
# which the LAN browser can't reach (ERR_CONNECTION_REFUSED).
# The lint exempts the literal '/' root page and explicit alias pairs
# (same function carrying both '/foo' and '/foo/'). See
# .agent/rules/neko-guide.md (§"API URL 末尾不带斜杠") and
# main_routers/characters_router.py docstring.
run: python scripts/check_api_trailing_slash.py
- name: Forbid trailing-slash /api/... URL literals (frontend)
# Counterpart to check_api_trailing_slash.py: the backend may declare
# /api/foo without trailing slash, but if frontend code calls
# fetch('/api/foo/') the same 307 → ERR_CONNECTION_REFUSED happens.
# Regex sniffer over static/, frontend/, templates/. Recognises
# prefix builders ('/api/foo/' + id, `/api/foo/${id}`) and exempts
# them; flags only standalone literals ending in '/'. Suppress with
# // noqa: API_TRAILING_SLASH if calling a third-party API that
# genuinely requires the slash.
run: python scripts/check_frontend_api_trailing_slash.py