Check json format in CI

This commit is contained in:
Fangliding
2026-05-25 21:37:31 +08:00
parent a2c2c5494e
commit f6bb9a7bc6
22 changed files with 350 additions and 298 deletions
+89
View File
@@ -0,0 +1,89 @@
# Validate that all ```json code blocks contain valid JSONC (JSON with comments).
import json
import re
import sys
from pathlib import Path
CODEBLOCK_START = re.compile(r"^```json\w*")
CODEBLOCK_END = re.compile(r"^```\s*$")
VITEPRESS_ANNOTATION = re.compile(r"\[!code [^\]]*\]")
def strip_jsonc_comments(text: str) -> str:
result = []
for line in text.splitlines():
stripped = line.lstrip()
if stripped.startswith("//"):
result.append("")
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 check_file(path: Path) -> list[str]:
errors = []
lines = path.read_text(encoding="utf-8").splitlines()
block_start = -1
for i, line in enumerate(lines):
stripped = line.strip()
if CODEBLOCK_START.match(stripped):
block_start = i
continue
if CODEBLOCK_END.match(stripped) and block_start >= 0:
block_lines = lines[block_start + 1 : i]
block_lines = [
VITEPRESS_ANNOTATION.sub("", l) for l in block_lines
]
fragment = "\n".join(block_lines)
cleaned = strip_jsonc_comments(fragment)
try:
json.loads(cleaned)
except json.JSONDecodeError as e:
rel = path.as_posix()
errors.append(f" {rel}:{block_start + 1} {e}")
block_start = -1
return errors
def main() -> int:
root = Path.cwd()
all_errors: list[str] = []
for md in sorted(root.rglob("*.md")):
text = md.read_text(encoding="utf-8")
if "```json" not in text:
continue
all_errors.extend(check_file(md))
if all_errors:
print(f"Found {len(all_errors)} invalid JSON block(s):\n")
for e in all_errors:
print(e)
return 1
print("All JSON blocks are valid JSONC.")
return 0
if __name__ == "__main__":
sys.exit(main())
+9 -16
View File
@@ -1,23 +1,16 @@
name: Post Check
on:
push:
branches: [main]
pull_request:
branches: [main]
on: [push, pull_request]
jobs:
check:
check-focus:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: actions/checkout@v6
- run: python .github/check-scripts/check_code_focus.py
- 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
check-json:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: python .github/check-scripts/check_json.py
+34 -36
View File
@@ -150,28 +150,26 @@ EDNS Client Subnet 扩展中使用的 IP 地址。
`UseSystem` 自适应操作系统网络环境。查询前分别检查是否有 IPv4 和 IPv6 的默认网关,以此限制所有服务器的能力并设置查询类型的默认值。在图形环境操作系统上实时检查,在命令行环境只检查一次。
```json
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": [
"geosite:netflix"
],
"skipFallback": true,
"queryStrategy": "UseIPv4" // netflix 的域名查询 A 记录
},
{
"address": "https://1.1.1.1/dns-query",
"domains": [
"geosite:openai"
],
"skipFallback": true,
"queryStrategy": "UseIPv6" // openai 的域名查询 AAAA 记录
}
],
"queryStrategy": "UseIP" // 全局同时查询 A 和 AAAA 记录
}
{
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": ["geosite:netflix"],
"skipFallback": true,
"queryStrategy": "UseIPv4" // netflix 的域名查询 A 记录
},
{
"address": "https://1.1.1.1/dns-query",
"domains": ["geosite:openai"],
"skipFallback": true,
"queryStrategy": "UseIPv6" // openai 的域名查询 AAAA 记录
}
],
"queryStrategy": "UseIP" // 全局同时查询 A 和 AAAA 记录
}
}
```
::: tip TIP 1
@@ -189,20 +187,20 @@ EDNS Client Subnet 扩展中使用的 IP 地址。
全局 `"queryStrategy": "UseIP"` 与 子项 `"queryStrategy": "UseIPv4"` 不冲突。
```json
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": [
"geosite:netflix"
],
"skipFallback": true,
"queryStrategy": "UseIPv6" // 全局 "UseIPv4" 与 子项 "UseIPv6" 冲突
}
],
"queryStrategy": "UseIPv4"
}
{
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": ["geosite:netflix"],
"skipFallback": true,
"queryStrategy": "UseIPv6" // 全局 "UseIPv4" 与 子项 "UseIPv6" 冲突
}
],
"queryStrategy": "UseIPv4"
}
}
```
子项 netflix 的域名查询由于 `"queryStrategy"` 值冲突,得到空响应。netflix 的域名由 `https://1.1.1.1/dns-query` 查询,得到 A 记录。
+2
View File
@@ -11,11 +11,13 @@ fallback 也可以将不同类型的流量根据 path 进行分流, 从而实现
## fallbacks 配置
```json
{
"fallbacks": [
{
"dest": 80
}
]
}
```
> `fallbacks`: \[ [FallbackObject](#fallbackobject) \]
+34 -36
View File
@@ -403,45 +403,43 @@ HTTP 请求头。
### 负载均衡配置示例
```json
"routing": {
"rules": [
{
"inboundTag": [
"in"
],
"balancerTag": "round"
}
],
"balancers" : [
{
"selector": [
"out"
],
"strategy": {
"type":"roundRobin"
},
"tag": "round"
}
]
},
"inbounds": [
{
// 入站配置
"tag": "in"
}
{
"routing": {
"rules": [
{
"inboundTag": ["in"],
"balancerTag": "round"
}
],
"outbounds": [
{
// 出站配置
"tag": "out1"
"balancers": [
{
"selector": ["out"],
"strategy": {
"type": "roundRobin"
},
{
// 出站配置
"tag": "out2"
}
"tag": "round"
}
]
},
"inbounds": [
{
// 入站配置
"tag": "in"
}
],
"outbounds": [
{
// 出站配置
"tag": "out1"
},
{
// 出站配置
"tag": "out2"
}
]
}
```
### 预定义域名列表
+1 -1
View File
@@ -112,7 +112,7 @@ iptables -t mangle -A OUTPUT -m owner ! --gid-owner 23333 -j XRAY_SELF
],
"outbounds": [
{
你的服务器配置
// 你的服务器配置
}
]
}
+6 -14
View File
@@ -96,17 +96,13 @@ lsmod | grep wireguard
```json
{
"api": {
"services": [
"HandlerService",
"LoggerService",
"StatsService"
],
"services": ["HandlerService", "LoggerService", "StatsService"],
"tag": "api"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": <port>,
"port": 12345, // <port>
"protocol": "dokodemo-door",
"settings": {
"rewriteAddress": "127.0.0.1"
@@ -128,13 +124,13 @@ lsmod | grep wireguard
"tag": "wg0",
"streamSettings": {
"sockopt": {
"mark": // <mark>
"mark": 255 // <mark>
}
},
"settings": {
"domainStrategy": "UseIPv6"
}
}, //设置fwmark为<mark>的用户走指定方式”UseIPv6””UseIPv4”
}, //设置fwmark为<mark>的用户走指定方式”UseIPv6””UseIPv4”
// <--请在不同的方案中选择--> 方案2:sendThrough
{
"tag": "wg0",
@@ -175,9 +171,7 @@ lsmod | grep wireguard
"routing": {
"rules": [
{
"inboundTag": [
"api"
],
"inboundTag": ["api"],
"outboundTag": "api"
},
{
@@ -189,9 +183,7 @@ lsmod | grep wireguard
},
{
"outboundTag": "blocked",
"protocol": [
"bittorrent"
]
"protocol": ["bittorrent"]
}
]
},
@@ -125,7 +125,7 @@ Linux 使用`Netfilter`来管理网络,`Netfilter`模型如下:
],
"outbounds": [
{
你的服务器配置
// 你的服务器配置
}
]
}
+3 -3
View File
@@ -70,7 +70,7 @@ bash -c "$(curl -L wgcf-cli.vercel.app)"
2. 运行 `wgcf-cli register` 进行注册,输出:
```json
```
❯ wgcf-cli register
{
"endpoint": {
@@ -151,7 +151,7 @@ bash -c "$(curl -L wgcf-cli.vercel.app)"
在现有路由中新增以下
```json
```
{
"domain": [
"geosite:cn"
@@ -195,7 +195,7 @@ bash -c "$(curl -L wgcf-cli.vercel.app)"
"protocol": "vmess",
"settings": {
"address": "我的IP",
"port": 我的端口,
"port": 12345, // 你的端口
"id": "我的UUID",
"security": "auto"
},
+34 -36
View File
@@ -150,28 +150,26 @@ The default value `UseIP` allows querying both A + AAAA records. When a query in
`UseSystem` adapts to the operating system's network environment. Before querying, it checks whether there are IPv4 and IPv6 default gateways, thereby limiting the capabilities of all servers and setting the default query type. It checks in real-time on graphical OS environments and only once on command-line environments.
```json
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": [
"geosite:netflix"
],
"skipFallback": true,
"queryStrategy": "UseIPv4" // netflix domain queries A record
},
{
"address": "https://1.1.1.1/dns-query",
"domains": [
"geosite:openai"
],
"skipFallback": true,
"queryStrategy": "UseIPv6" // openai domain queries AAAA record
}
],
"queryStrategy": "UseIP" // Globally query both A and AAAA records
}
{
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": ["geosite:netflix"],
"skipFallback": true,
"queryStrategy": "UseIPv4" // netflix domain queries A record
},
{
"address": "https://1.1.1.1/dns-query",
"domains": ["geosite:openai"],
"skipFallback": true,
"queryStrategy": "UseIPv6" // openai domain queries AAAA record
}
],
"queryStrategy": "UseIP" // Globally query both A and AAAA records
}
}
```
::: tip TIP 1
@@ -189,20 +187,20 @@ Global `"queryStrategy": "UseIP"` does not conflict with sub-item `"queryStrateg
Global `"queryStrategy": "UseIP"` does not conflict with sub-item `"queryStrategy": "UseIPv4"`.
```json
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": [
"geosite:netflix"
],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Global "UseIPv4" conflicts with sub-item "UseIPv6"
}
],
"queryStrategy": "UseIPv4"
}
{
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": ["geosite:netflix"],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Global "UseIPv4" conflicts with sub-item "UseIPv6"
}
],
"queryStrategy": "UseIPv4"
}
}
```
The sub-item query for the Netflix domain returns an empty response due to the conflicting `"queryStrategy"` value. The Netflix domain is then queried by `https://1.1.1.1/dns-query`, returning an A record.
+2
View File
@@ -11,11 +11,13 @@ Currently, you can use the fallback feature by configuring `fallbacks` when usin
## Fallbacks Configuration
```json
{
"fallbacks": [
{
"dest": 80
}
]
}
```
> `fallbacks`: \[ [FallbackObject](#fallbackobject) \]
+34 -36
View File
@@ -403,45 +403,43 @@ Weight value. The larger the value, the less likely the corresponding node is to
### Load Balancer Configuration Example
```json
"routing": {
"rules": [
{
"inboundTag": [
"in"
],
"balancerTag": "round"
}
],
"balancers" : [
{
"selector": [
"out"
],
"strategy": {
"type":"roundRobin"
},
"tag": "round"
}
]
},
"inbounds": [
{
// Inbound config
"tag": "in"
}
{
"routing": {
"rules": [
{
"inboundTag": ["in"],
"balancerTag": "round"
}
],
"outbounds": [
{
// Outbound config
"tag": "out1"
"balancers": [
{
"selector": ["out"],
"strategy": {
"type": "roundRobin"
},
{
// Outbound config
"tag": "out2"
}
"tag": "round"
}
]
},
"inbounds": [
{
// Inbound config
"tag": "in"
}
],
"outbounds": [
{
// Outbound config
"tag": "out1"
},
{
// Outbound config
"tag": "out2"
}
]
}
```
### Predefined Domain List
+1 -1
View File
@@ -108,7 +108,7 @@ Configure Xray `dokodemo-door` to listen on port 12345, enable `followRedirect`
],
"outbounds": [
{
Your Server Configuration
// Your Server Configuration
}
]
}
+6 -14
View File
@@ -95,17 +95,13 @@ lsmod | grep wireguard
```json
{
"api": {
"services": [
"HandlerService",
"LoggerService",
"StatsService"
],
"services": ["HandlerService", "LoggerService", "StatsService"],
"tag": "api"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": <port>,
"port": 12345, // <port>
"protocol": "dokodemo-door",
"settings": {
"rewriteAddress": "127.0.0.1"
@@ -127,13 +123,13 @@ lsmod | grep wireguard
"tag": "wg0",
"streamSettings": {
"sockopt": {
"mark": // <mark>
"mark": 255 // <mark>
}
},
"settings": {
"domainStrategy": "UseIPv6"
}
}, // Users with fwmark set to <mark> use the specified strategy "UseIPv6" or "UseIPv4"
}, // Users with fwmark set to <mark> use the specified strategy "UseIPv6" or "UseIPv4"
// <--Please choose between different schemes--> Scheme 2: sendThrough
{
"tag": "wg0",
@@ -174,9 +170,7 @@ lsmod | grep wireguard
"routing": {
"rules": [
{
"inboundTag": [
"api"
],
"inboundTag": ["api"],
"outboundTag": "api"
},
{
@@ -188,9 +182,7 @@ lsmod | grep wireguard
},
{
"outboundTag": "blocked",
"protocol": [
"bittorrent"
]
"protocol": ["bittorrent"]
}
]
},
@@ -125,7 +125,7 @@ The configuration file should listen on port 12345 and enable tproxy:
],
"outbounds": [
{
Your_Server_Configuration
// Your Server Configuration
}
]
}
+3 -3
View File
@@ -70,7 +70,7 @@ bash -c "$(curl -L wgcf-cli.vercel.app)"
1. Run `wgcf-cli register` to register. Output:
```json
```
❯ wgcf-cli register
{
"endpoint": {
@@ -151,7 +151,7 @@ Recommended routing strategy: `IPIfNonMatch`.
Add the following to your existing routing rules:
```json
```
{
"domain": [
"geosite:cn"
@@ -195,7 +195,7 @@ Add the following to your existing routing rules:
"protocol": "vmess",
"settings": {
"address": "My_Server_IP",
"port": My_Port,
"port": 12345, // My_Port
"id": "My_UUID",
"security": "auto"
},
+34 -36
View File
@@ -147,28 +147,26 @@ IP-адрес, используемый в расширении EDNS Client Subn
`UseSystem` адаптируется к сетевой среде операционной системы. Перед запросом проверяется наличие шлюзов по умолчанию для IPv4 и IPv6, чтобы ограничить возможности серверов и установить тип запроса. В ОС с графическим интерфейсом проверка выполняется в реальном времени, в среде командной строки — только один раз.
```json
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": [
"geosite:netflix"
],
"skipFallback": true,
"queryStrategy": "UseIPv4" // Для доменов netflix запрашивать A запись
},
{
"address": "https://1.1.1.1/dns-query",
"domains": [
"geosite:openai"
],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Для доменов openai запрашивать AAAA запись
}
],
"queryStrategy": "UseIP" // Глобально запрашивать одновременно A и AAAA записи
}
{
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": ["geosite:netflix"],
"skipFallback": true,
"queryStrategy": "UseIPv4" // Для доменов netflix запрашивать A запись
},
{
"address": "https://1.1.1.1/dns-query",
"domains": ["geosite:openai"],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Для доменов openai запрашивать AAAA запись
}
],
"queryStrategy": "UseIP" // Глобально запрашивать одновременно A и AAAA записи
}
}
```
::: tip TIP 1
@@ -186,20 +184,20 @@ IP-адрес, используемый в расширении EDNS Client Subn
Глобальный `"queryStrategy": "UseIP"` и вложенный `"queryStrategy": "UseIPv4"` — не конфликтуют.
```json
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": [
"geosite:netflix"
],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Конфликт: глобальный "UseIPv4" и "UseIPv6" вложенного элемента
}
],
"queryStrategy": "UseIPv4"
}
{
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://8.8.8.8/dns-query",
"domains": ["geosite:netflix"],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Конфликт: глобальный "UseIPv4" и "UseIPv6" вложенного элемента
}
],
"queryStrategy": "UseIPv4"
}
}
```
Запрос домена Netflix получит пустой ответ из-за конфликта значений `"queryStrategy"`. Домен Netflix будет запрошен через `https://1.1.1.1/dns-query` и получит запись A.
+2
View File
@@ -11,11 +11,13 @@ Fallback также может разделять трафик различны
## Настройка `fallbacks`
```json
{
"fallbacks": [
{
"dest": 80
}
]
}
```
> `fallbacks`: \[ [FallbackObject](#fallbackobject) \]
+14 -14
View File
@@ -106,20 +106,20 @@ MTU нижнего уровня tun в Wireguard.
Примечание: В настройках `Freedom` доступны опции, такие как `UseIP`, которые здесь отсутствуют, так как Wireguard требует наличия действительного IP-адреса.
```json
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://1.1.1.1/dns-query",
"domains": [
"geosite:openai"
],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Запрос только AAAA-записей
}
],
"queryStrategy": "UseIP" // Запрос A и AAAA одновременно. Если не указано, используется значение по умолчанию UseIP.
}
{
"dns": {
"servers": [
"https://1.1.1.1/dns-query",
{
"address": "https://1.1.1.1/dns-query",
"domains": ["geosite:openai"],
"skipFallback": true,
"queryStrategy": "UseIPv6" // Запрос только AAAA-записей
}
],
"queryStrategy": "UseIP" // Запрос A и AAAA одновременно. Если не указано, используется значение по умолчанию UseIP.
}
}
```
### Peers
+31 -33
View File
@@ -395,42 +395,40 @@ URL, по которому будет отправлено уведомлени
### Примеры конфигурации балансировки нагрузки
```json
"routing": {
"rules": [
{
"inboundTag": [
"in"
],
"balancerTag": "round"
}
],
"balancers" : [
{
"selector": [
"out"
],
"strategy": {
"type":"roundRobin"
},
"tag": "round"
}
]
},
"inbounds": [
{
"tag": "in"
}
{
"routing": {
"rules": [
{
"inboundTag": ["in"],
"balancerTag": "round"
}
],
"outbounds": [
{
"tag": "out1"
"balancers": [
{
"selector": ["out"],
"strategy": {
"type": "roundRobin"
},
{
"tag": "out2"
}
"tag": "round"
}
]
},
"inbounds": [
{
"tag": "in"
}
],
"outbounds": [
{
"tag": "out1"
},
{
"tag": "out2"
}
]
}
```
### Предопределенные списки доменов
+6 -14
View File
@@ -95,17 +95,13 @@ lsmod | grep wireguard
```json
{
"api": {
"services": [
"HandlerService",
"LoggerService",
"StatsService"
],
"services": ["HandlerService", "LoggerService", "StatsService"],
"tag": "api"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": <port>,
"port": 12345, // <port>
"protocol": "dokodemo-door",
"settings": {
"rewriteAddress": "127.0.0.1"
@@ -127,13 +123,13 @@ lsmod | grep wireguard
"tag": "wg0",
"streamSettings": {
"sockopt": {
"mark": <mark>
"mark": 255 // <mark>
}
},
"settings": {
"domainStrategy": "UseIPv6"
}
}, // Трафик с меткой fwmark, равной <mark>, будет направлен через UseIPv6/UseIPv4.
}, // Трафик с меткой fwmark, равной <mark>, будет направлен через UseIPv6/UseIPv4.
// <--Выберите один из вариантов--> Вариант 2: sendThrough
{
"tag": "wg0",
@@ -174,9 +170,7 @@ lsmod | grep wireguard
"routing": {
"rules": [
{
"inboundTag": [
"api"
],
"inboundTag": ["api"],
"outboundTag": "api"
},
{
@@ -189,9 +183,7 @@ lsmod | grep wireguard
},
{
"outboundTag": "blocked",
"protocol": [
"bittorrent"
]
"protocol": ["bittorrent"]
}
]
},
+3 -3
View File
@@ -75,7 +75,7 @@ bash -c "$(curl -L wgcf-cli.vercel.app)"
2. Запустите `wgcf-cli register` для регистрации. Вывод:
```json
```
❯ wgcf-cli register
{
"endpoint": {
@@ -157,7 +157,7 @@ bash -c "$(curl -L wgcf-cli.vercel.app)"
Добавьте следующие правила к существующим правилам маршрутизации:
```json
```
{
"domain": [
"geosite:cn"
@@ -201,7 +201,7 @@ bash -c "$(curl -L wgcf-cli.vercel.app)"
"protocol": "vmess",
"settings": {
"address": "IP-адрес",
"port": Порт,
"port": 12345, // Порт
"id": "UUID",
"security": "auto"
},