Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ jobs:
ARTIFACT_GENERATION_ID: ${{ github.run_id }}-${{ github.run_attempt }}
ARTIFACT_BUILD_ID: ${{ github.run_id }}
ARTIFACT_SOURCE_SHA: ${{ github.sha }}
ARTIFACT_BASELINE_FILE: ${{ steps.scope.outputs.baseline_file }}
run: ./scripts/commands/build-artifacts-transaction.sh

- name: Show summary
Expand Down
11 changes: 5 additions & 6 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ jobs:
fetch-depth: 0
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: '3.11'

- name: Select release candidate scope
id: scope
env:
Expand All @@ -53,12 +58,6 @@ jobs:
- name: Show selected release candidate scope
run: echo "${{ steps.scope.outputs.scope }} - ${{ steps.scope.outputs.reason }}"

- name: Set up Python
if: steps.scope.outputs.scope != 'none'
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: '3.11'

- name: Prepare scripts
if: steps.scope.outputs.scope != 'none'
run: find scripts -type f \( -name '*.sh' -o -name '*.py' \) -exec chmod +x {} +
Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
SHELL := /usr/bin/env bash
REQUIRE_SHELLCHECK ?= 0
BASH_MIN_MAJOR := 5

SHELL_SCRIPTS := $(shell find scripts -type f -name '*.sh' | sort)
PYTHON_TOOLS := $(shell find scripts/tools -type f -name '*.py' | sort)
Expand All @@ -9,7 +8,7 @@ PYTHON_TOOLS := $(shell find scripts/tools -type f -name '*.py' | sort)

help:
@echo "Available targets:"
@echo " make check-runtime Verify the supported Bash runtime"
@echo " make check-runtime Verify the supported Bash and Python runtimes"
@echo " make lint Run shell, Python, and custom rule lint checks"
@echo " make test Run all repository test scripts"
@echo " make validate Run lint and tests"
Expand All @@ -19,7 +18,7 @@ help:
@echo " make clean Remove generated artifacts and temporary files"

check-runtime:
@bash -c 'if (( BASH_VERSINFO[0] < $(BASH_MIN_MAJOR) )); then echo "Bash $(BASH_MIN_MAJOR)+ is required (found $$BASH_VERSION)" >&2; exit 1; fi'
@./scripts/commands/check-runtime.sh

lint: check-runtime lint-shell lint-python lint-config lint-rules

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ https://raw.githubusercontent.com/KuGouGo/Rules/mihomo/ip/google.mrs

## 本地维护

本地命令要求 Bash 5+、GNU Make、GitPython 3。macOS 可使用 Homebrew Bash 运行检查和文本构建;需要下载 sing-box 或 mihomo 的二进制构建只支持 lock 文件声明的 Linux 平台。
本地命令要求 Bash 5+、Python 3.11+、GNU Make 和 Git。macOS 可使用 Homebrew Bash 与 Python 运行检查和文本构建;需要下载 sing-box 或 mihomo 的二进制构建只支持 lock 文件声明的 Linux 平台。

```bash
make check-runtime
Expand Down
21 changes: 13 additions & 8 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@

## 环境支持

CI 的受支持基准是 GitHub Actions `ubuntu-latest`、Bash 5+ 和 Python 3.11。本地验证与文本构建支持安装了 Bash 5+、GNU Make、Git、Python 3、curl、tar、gzip、find 的 Linux、WSL 和 macOS;macOS 应使用 Homebrew Bash,并确保 `/opt/homebrew/bin` 或 `/usr/local/bin` 位于 `/bin` 之前。`make check-runtime` 会在构建前拒绝旧版 Bash。
CI 的受支持基准是 GitHub Actions `ubuntu-latest`、Bash 5+ 和 Python 3.11。本地验证与文本构建支持安装了 Bash 5+、Python 3.11+、GNU Make、Git、curl、tar、gzip、find 的 Linux、WSL 和 macOS;macOS 应使用 Homebrew Bash 与 Python,并确保 `/opt/homebrew/bin` 或 `/usr/local/bin` 位于系统路径之前。`make check-runtime` 会在构建前拒绝旧版 Bash 或 Python。

Homebrew 的版本化 Python 将通用的 `python3` 链接放在 `libexec/bin`。macOS 可用以下配置验证当前终端,而无需为本地系统增加二进制规则编译支持:

```bash
export PATH="$(brew --prefix python@3.11)/libexec/bin:$(brew --prefix)/bin:$PATH"
make check-runtime
```

二进制工具下载逻辑仅支持 Linux 的 `amd64`、`arm64` 锁定资产。原生 Windows 在需要下载 sing-box/mihomo 时会被 `require_non_windows_shell` 明确拒绝;macOS 也没有对应 lock 平台。请使用 Linux、WSL 或 GitHub Actions 完成二进制构建。

Expand All @@ -19,22 +26,20 @@ make test
make validate
make build-custom-text
make build-custom
ARTIFACT_GENERATION_ID=local-1 ARTIFACT_BUILD_SCOPE=full ./scripts/commands/generate-artifact-manifest.sh
./scripts/commands/verify-artifact-manifest.sh
make preflight
make clean
```

- `make validate`:Shell 语法、可用时的 ShellCheck、Python 编译、配置、自定义规则和测试。
- `make check-runtime`:验证当前 `PATH` 解析到 Bash 5+。
- `make check-runtime`:验证当前 `PATH` 解析到 Bash 5+ 和 Python 3.11+
- `make build-custom-text`:只生成自定义文本产物,不下载二进制编译器。
- `make build-custom`:生成自定义文本和二进制产物。
- `make preflight`:`make validate` 加文本自定义构建;不执行完整同步、产物守卫(artifact guard)或发布。
- `build-artifacts-transaction.sh`:CI 的完整入口。它在 `.tmp/` 中以事务自有的 `RULES_ARTIFACT_ROOT` 组合上游同步或已发布分支恢复、自定义构建、守卫、摘要、manifest 生成与验证;调用方提供 `RULES_ARTIFACT_ROOT` 会被拒绝,测试或运维如需改变最终提升位置应使用 `RULES_LIVE_ARTIFACT_ROOT`。全部成功后才以目录替换提升为 `.output/`(或该显式 live root)。失败诊断写入非发布目录 `.artifacts/diagnostics/`,旧 live root 保持不变。`config/upstreams.json` 为每个源声明 parser、required/optional、原始字节、规范条目、地址族和 fallback policy;required 源在主 URL 与允许的回退均失败,或出现语义健康回归时阻断提升。每个 RIPE Stat ASN 响应和合并分组都使用 `ripe-stat` health policy,过小或无效响应会先写入诊断摘要再阻断事务。自定义恢复要求五个发布分支都存在且具有相同 generation/source 身份,并把分支 commit 写入 manifest restoration metadata;缺失或身份分裂时失败关闭,应执行 full 构建恢复发布 cohort。
- `generate-artifact-manifest.sh`:在构建与守卫完成后、写 manifest 前,按能力配置中的 `verifier` 分派器验证每个产物;缺失或未验证的二进制会阻断生成。默认位于 `.output/`,事务内服从 `RULES_ARTIFACT_ROOT`。调用方应明确提供 generation id、build scope,CI 还将 source SHA 绑定到实际 checkout 的 `github.sha`:PR 验证记录被测试的合并提交,正式发布记录 `main` 提交。
- `verify-artifact-manifest.sh`:严格重算所选 artifact root 内的可发布文件集合、路径、大小和 SHA-256,并重新执行产物验证、核对能力/lock 与可选 source SHA;发布 job 在恢复或安装锁定工具后强制执行同一验证。
- `build-artifacts-transaction.sh`:CI 的完整入口。它在 `.tmp/` 中以事务自有的 `RULES_ARTIFACT_ROOT` 组合上游同步或已发布分支恢复、自定义构建、守卫、摘要、manifest 生成与验证;调用方提供 `RULES_ARTIFACT_ROOT` 会被拒绝,测试或运维如需改变最终提升位置应使用 `RULES_LIVE_ARTIFACT_ROOT`。该 live root 必须与仓库 `.tmp/` 位于同一文件系统,跨设备目标会在构建前被拒绝;最终 backup、promotion 和 rollback 均使用不允许复制回退的严格目录 rename,因此检查后的设备变化也会以 EXDEV 失败。全部成功后才提升为 `.output/`(或该显式 live root)。backup 后收到 HUP、INT 或 TERM,以及 promotion rename 失败时,都会通过同一幂等回滚恢复旧目录;恢复本身失败时事务目录保留唯一备份以供人工处理。失败诊断写入非发布目录 `.artifacts/diagnostics/`,并记录 failure reason、promotion state、rollback status 与可用的 signal。`config/upstreams.json` 为每个源声明 parser、required/optional、原始字节、规范条目、地址族和 fallback policy;required 源在主 URL 与允许的回退均失败,或出现语义健康回归时阻断提升。每个 RIPE Stat ASN 响应和合并分组都使用 `ripe-stat` health policy,过小或无效响应会先写入诊断摘要再阻断事务。自定义恢复要求五个发布分支都存在且具有相同 generation/source 身份,并把分支 commit 写入 manifest restoration metadata;缺失或身份分裂时失败关闭,应执行 full 构建恢复发布 cohort。
- `generate-artifact-manifest.sh`:完整构建事务的内部阶段,不是独立的日常构建入口。它要求当前 artifact root 已有 canonical 输入、摘要、来源记录和发布基线;按能力配置验证五个平台后才写入 schema v4 manifest。缺失或未验证的产物会阻断生成。CI source SHA 绑定到实际 checkout 的 `github.sha`:PR 验证记录被测试的合并提交,正式发布记录 `main` 提交。
- `verify-artifact-manifest.sh`:严格重算所选 artifact root 内的可发布文件集合、路径、大小和 SHA-256,并重新执行五平台 canonical 验证、核对发布基线、能力/lock 与可选 source SHA;发布 job 在恢复或安装锁定工具后强制执行同一验证。需要单独调试时,应先保留完整事务生成的 artifact root,不要手工拼装 manifest 参数

二进制验证使用固定工具的真实读回接口:`.srs` 执行 `sing-box rule-set decompile` 并解析 JSON`.mrs` 执行 `mihomo convert-ruleset <domain|ipcidr> mrs INPUT OUTPUT`。读回结果与同名 custom 源或同一事务的 Egern/Surge 文本产物比较规范化语义集合:域名消除已被更宽后缀覆盖的冗余项,IP 合并为等价 CIDR 并集;值替换、范围扩大或范围丢失都会失败。manifest 记录验证方法、原始计数、读回语义 SHA-256,以及规范输入的语义 SHA-256。
五平台验证以 `.output/.canonical/{domain,ip}/` 为共同基准。Surge、Quantumult X、Egern 解析各自文本/YAML,`.srs` 使用固定的 `sing-box rule-set decompile` 读回 JSON`.mrs` 使用固定的 `mihomo convert-ruleset <domain|ipcidr> mrs INPUT OUTPUT` 读回。每个平台先按能力矩阵过滤不支持的类型,再比较规范化语义集合:域名消除已被更宽后缀覆盖的冗余项,IP 合并为等价 CIDR 并集;值替换、范围扩大、范围丢失、缺少副本和没有 canonical 来源的额外文件都会失败。manifest 记录验证方法、原始计数、读回语义 SHA-256,以及规范输入的语义 SHA-256。
- `make clean`:删除 `.tmp/`、`.output/`、`.artifacts/`、Python `__pycache__` 和未完成的 `.bin/*.new*`;保留已安装的 `.bin/sing-box`、`.bin/mihomo` 及 provenance sidecar。

CI 设置 `REQUIRE_SHELLCHECK=1`,本地缺少 ShellCheck 时的跳过不代表 CI 会通过。
Expand Down
12 changes: 8 additions & 4 deletions docs/STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
| `scripts/tests/`、`tests/fixtures/` | 自动测试与稳定夹具 |
| `templates/branch-readmes/` | 发布分支 README 模板及随发布树携带的 v2fly MIT 充分通知 |
| `.output/` | 构建产物及部分审计摘要 |
| `.output/.canonical/` | 事务内保留的 domain/IP 规范编译输入,只用于跨平台语义校验,不进入发布分支 |
| `.tmp/` | 可清理临时工作区 |
| `.bin/` | 外部工具及版本缓存 |
| `.artifacts/` | 失败构建保留的诊断摘要;属于可清理的本地/CI 数据,不进入发布分支 |

## 构建范围

完整范围依次同步主上游、构建自定义规则、执行产物守卫(artifact guard),再上传并发布。自定义范围从五个发布分支恢复既有产物,然后重建自定义规则。工作流没有独立的 Fake-IP 同步步骤。
完整范围依次同步主上游、写入 `.output/.canonical/`、构建自定义规则、执行产物守卫(artifact guard),再上传并发布。自定义范围从五个发布分支恢复同一发布 cohort;旧 cohort 没有 canonical 状态时先从 Egern 文本产物重建,再用本次自定义源覆盖相应规范输入并重建产物。工作流没有独立的 Fake-IP 同步步骤。

`fakeip-filter` 当前源为本仓库维护的 `sources/custom/domain/fakeip-filter.list`,由 `build-custom.sh` 与其他自定义规则一起生成五平台形式,不从网络下载预编译文件。`config/upstreams.json` 覆盖主上游网络输入;工具资产下载另由工具 lock 控制。

Expand All @@ -31,11 +32,14 @@
- `.output/upstream-summary.json`:主同步在健康检查后记录名称、实际 URL、状态、回退、临时路径、原始与规范化内容的字节数、条目数和 SHA-256;DLC 另含检出的 commit。
- `.output/domain/rule-manifest.json`:域名列表、区域集合和属性派生结构。
- `.output/build-summary.json`:GitHub Actions 在产物守卫之后、规范发布清单之前扫描 `.output/` 生成。
- `.output/artifact-manifest.json`:规范发布清单,包含 schema/generation/build/source/build scope、能力与工具 lock 摘要、工具 provenance metadata、可用的上游/构建摘要,以及每个可发布 domain/ip 文件的平台、类型、扩展名、字节数、SHA-256 和可判定来源。JSON 键与产物按稳定顺序输出;generation/build id 由调用方提供,因此相同输入和 id 可复现相同内容。
- `.output/.canonical/{domain,ip}/`:规范化、去重后的平台无关规则集合。它是五个平台语义验证的唯一比较基准;manifest 记录验证结果,但不会将该目录列为发布产物。
- `.output/artifact-manifest.json`:schema v4 规范发布清单,包含 generation/build/source/build scope、完整发布基线(整体状态以及各分支 commit/generation/source)、能力与工具 lock 摘要、工具 provenance metadata、可用的上游/构建摘要,以及每个可发布 domain/ip 文件的平台、类型、扩展名、字节数、SHA-256 和可判定来源。JSON 键与产物按稳定顺序输出;generation/build id 由调用方提供,因此相同输入和 id 可复现相同内容。
- `.tmp/**/normalize-tasks.json`:批处理任务描述,属于临时数据。
- `.artifacts/diagnostics/<generation-time>/`:失败事务保留的 `transaction-health.json` 及可用的构建/上游摘要;CI 日志只展示白名单内且大小受限的 JSON,完整诊断作为短期 Actions artifact 上传。

`verify-artifact-manifest.sh` 严格重算能力矩阵允许的完整文件集合、路径层级与安全性、非零大小、字节数和 SHA-256,并核对能力/lock 摘要及可选的预期 source SHA。二进制读回规则与同名 custom 源或同一事务的文本产物比较等价语义集合:域名后缀覆盖和 CIDR 并集合并允许编译器消除冗余,但值替换、扩大或丢失范围会失败;清单同时记录语义 SHA-256。发布作业下载后再次验证;`publish-branches.sh` 自身也必须先验证清单,拒绝缺失、额外或被修改的产物。清单只作为流水线审计输入,不复制到发布分支。一次发布中五个分支提交携带共同 generation id 和 source SHA;任一平台 tree 改变时完整 cohort 原子推进并保留各分支父历史,全部 tree 不变时整体跳过。
`verify-artifact-manifest.sh` 严格重算能力矩阵允许的完整文件集合、路径层级与安全性、非零大小、字节数和 SHA-256,并核对能力/lock 摘要及可选的预期 source SHA。Surge、Quantumult X、Egern、sing-box 和 Mihomo 都会按各自能力过滤规则类型,再与 `.output/.canonical/` 比较等价语义集合;域名后缀覆盖和 CIDR 并集合并允许编译器消除冗余,但值替换、扩大或丢失范围会失败。缺少任一应有平台副本、存在没有 canonical 来源的额外产物,或为只含该平台不支持规则的空集合生成文件,同样失败。清单同时记录读回方法、计数和语义 SHA-256。

发布作业下载后再次验证;`publish-branches.sh` 自身也必须先验证清单,拒绝缺失、额外、被修改或重放的产物。清单只作为流水线审计输入,不复制到发布分支。一次发布中五个分支提交携带共同 generation id 和 source SHA;任一平台 tree 改变时完整 cohort 原子推进并保留各分支父历史,全部 tree 不变时在重新读取远端 cohort 后整体跳过。自定义范围以最近一次一致发布 cohort 的 source 为累计比较基准,而不是只比较 `HEAD^`,因此连续的文档提交不会掩盖此前未发布的规则改动。候选 source/generation 不得早于远端基线;准备前、推送前和推送后均检查远端 `main`,五个产物 ref 使用带预期 SHA lease 的原子推送。Git 协议无法把未变化的 `main` ref 纳入同一次 compare-and-swap;若推送产物后发现 `main` 已前进,本次运行会明确失败,由当前 `main` 的排队运行继续推进。

`scripts/tools/artifact_origins.py` 是 `artifact-origins.json` 的唯一写入口:完整同步重置为 `generated-upstream`,发布分支恢复重置为 `restored-published-branch`,自定义构建只重标本次控制且实际存在的目标,并清除对应平台已经删除或降级省略的旧记录。

Expand All @@ -62,7 +66,7 @@
- 两个平台部分内置 IP 集的总数与 IPv4/IPv6 最低值;
- Surge 上部分内置 IP 集相对基线的增长或删除检查。

artifact guard 本身不解析 `.srs` / `.mrs`,二进制读回与精确语义关联由随后生成和复验 manifest 的阶段执行;两者都不审查许可。发布脚本另行检查发布树、扩展名和本地产物完整性。
artifact guard 本身不执行五平台 canonical 语义比较;该检查由随后生成和复验 manifest 的阶段执行,其中 `.srs` / `.mrs` 使用锁定工具真实读回。两者都不审查许可。发布脚本另行检查发布树、扩展名和本地产物完整性。

## 工具缓存与清理

Expand Down
Loading