mirror of
https://github.com/XTLS/Xray-docs-next.git
synced 2026-09-22 22:38:05 +03:00
Check json focus in CI
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# Validate that lines covered by // [!code focus:N] form valid JSONC when wrapped in {}.
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
FOCUS_RE = re.compile(r"//\s*\[!code focus:(\d+)\]")
|
||||
CODEBLOCK_START = re.compile(r"^```json\w*")
|
||||
CODEBLOCK_END = re.compile(r"^```\s*$")
|
||||
|
||||
|
||||
def strip_jsonc_comments(text: str) -> str:
|
||||
result = []
|
||||
for line in text.splitlines():
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("//"):
|
||||
continue
|
||||
if "//" in line:
|
||||
in_str = False
|
||||
escape = False
|
||||
cut = -1
|
||||
for i, ch in enumerate(line):
|
||||
if escape:
|
||||
escape = False
|
||||
continue
|
||||
if ch == "\\":
|
||||
escape = True
|
||||
continue
|
||||
if ch == '"':
|
||||
in_str = not in_str
|
||||
if not in_str and line[i : i + 2] == "//":
|
||||
cut = i
|
||||
break
|
||||
if cut >= 0:
|
||||
line = line[:cut]
|
||||
result.append(line)
|
||||
return "\n".join(result)
|
||||
|
||||
|
||||
def validate_jsonc(fragment: str) -> str | None:
|
||||
import json
|
||||
|
||||
cleaned = strip_jsonc_comments(fragment)
|
||||
wrapped = "{\n" + cleaned + "\n}"
|
||||
try:
|
||||
json.loads(wrapped)
|
||||
return None
|
||||
except json.JSONDecodeError as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def check_file(path: Path) -> list[str]:
|
||||
errors = []
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
in_json_block = False
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
stripped = line.strip()
|
||||
if CODEBLOCK_START.match(stripped):
|
||||
in_json_block = True
|
||||
continue
|
||||
if CODEBLOCK_END.match(stripped):
|
||||
in_json_block = False
|
||||
continue
|
||||
if not in_json_block:
|
||||
continue
|
||||
|
||||
m = FOCUS_RE.search(line)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
n = int(m.group(1))
|
||||
start = i + 1
|
||||
end = min(start + n, len(lines))
|
||||
fragment = "\n".join(lines[start:end])
|
||||
err = validate_jsonc(fragment)
|
||||
if err:
|
||||
rel = path.as_posix()
|
||||
errors.append(f" {rel}:{i + 1} focus:{n} {err}")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
docs_root = Path.cwd()
|
||||
all_errors: list[str] = []
|
||||
|
||||
for md in sorted(docs_root.rglob("*.md")):
|
||||
text = md.read_text(encoding="utf-8")
|
||||
if "[!code focus:" not in text:
|
||||
continue
|
||||
errs = check_file(md)
|
||||
all_errors.extend(errs)
|
||||
|
||||
if all_errors:
|
||||
print(f"Found {len(all_errors)} invalid focus block(s):\n")
|
||||
for e in all_errors:
|
||||
print(e)
|
||||
return 1
|
||||
|
||||
print("All focus blocks are valid JSONC.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Post Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Check code focus blocks
|
||||
run: python .github/check-scripts/check_code_focus.py
|
||||
@@ -158,7 +158,6 @@ FakeDNS 本质上是一个 [DNS 服务器](./dns.md#serverobject),能够与任
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
// [!code focus:13]
|
||||
{
|
||||
"address": "fakedns",
|
||||
"domains": [
|
||||
|
||||
@@ -158,7 +158,6 @@ When using DNS routing (traffic splitting), to ensure `fakedns` has high priorit
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
// [!code focus:13]
|
||||
{
|
||||
"address": "fakedns",
|
||||
"domains": [
|
||||
|
||||
@@ -161,7 +161,6 @@ FakeDNS будет использовать этот блок IP-адресов
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
// [!code focus:13]
|
||||
{
|
||||
"address": "fakedns",
|
||||
"domains": [
|
||||
|
||||
@@ -14,7 +14,7 @@ VLESS - это легкий транспортный протокол без с
|
||||
{
|
||||
// ...
|
||||
"protocol": "vless",
|
||||
// [!code focus:17]
|
||||
// [!code focus:18]
|
||||
"settings": {
|
||||
"flow": "xtls-rprx-vision",
|
||||
"users": [
|
||||
|
||||
@@ -16,7 +16,7 @@ VMess полагается на системное время. Убедитес
|
||||
{
|
||||
// ...
|
||||
"protocol": "vmess",
|
||||
// [!code focus:8]
|
||||
// [!code focus:6]
|
||||
"settings": {
|
||||
"id": "5783a3e7-e373-51cd-8642-c83782b807c5",
|
||||
"security": "auto",
|
||||
|
||||
Reference in New Issue
Block a user