Skip to content

Commit 4fcde56

Browse files
Merge pull request #105 from loss-and-quick/ci/core-compat-gate
ci(release): gate every core bump — auto and manual — on config validation
2 parents 0e11c08 + dc03586 commit 4fcde56

3 files changed

Lines changed: 165 additions & 0 deletions

File tree

.github/workflows/core-compat.yml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Core compatibility
2+
3+
# Runs the config-validation harness against real cores so a core version whose
4+
# schema drifted from our config generators is caught. Two entry points:
5+
# - pull_request touching scripts/core-versions.sh: validates the versions the
6+
# PR pins, so a MANUAL bump (not just the release auto-bump) can't merge a pin
7+
# our generators don't match.
8+
# - schedule / workflow_dispatch: validates the LATEST upstream cores for early
9+
# warning (a deduped tracking issue) before the release ever bumps to them.
10+
# The release auto-bump is additionally gated in release.yml.
11+
on:
12+
pull_request:
13+
# Anything that can change whether a real core accepts our generated configs:
14+
# the pinned versions (a manual bump), how cores are staged, or the generators.
15+
paths:
16+
- scripts/core-versions.sh
17+
- scripts/fetch-cores-desktop.sh
18+
- crates/kasumi-core/**
19+
schedule:
20+
- cron: "0 0 * * 0" # Sunday — a day before the Monday release cron
21+
workflow_dispatch:
22+
23+
permissions:
24+
contents: read
25+
issues: write
26+
27+
# One in-flight run per ref; a new push/schedule supersedes the previous.
28+
concurrency:
29+
group: core-compat-${{ github.ref }}
30+
cancel-in-progress: true
31+
32+
jobs:
33+
check:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
37+
38+
- name: Setup Rust
39+
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
40+
with:
41+
toolchain: stable
42+
targets: x86_64-unknown-linux-gnu
43+
44+
- name: Resolve target core versions
45+
id: versions
46+
env:
47+
GH_TOKEN: ${{ github.token }}
48+
run: |
49+
if [ "${{ github.event_name }}" = "pull_request" ]; then
50+
# Validate exactly what the PR pins (catches a hand-edited bump).
51+
# shellcheck source=scripts/core-versions.sh
52+
. scripts/core-versions.sh
53+
xray="$XRAY_VERSION"; sb="$SINGBOX_VERSION"
54+
echo "Validating PR-pinned cores: xray $xray, sing-box $sb"
55+
else
56+
# Early warning: validate what the next release would bump TO.
57+
xray=$(gh release view --repo XTLS/Xray-core --json tagName -q .tagName)
58+
sb=$(gh release view --repo SagerNet/sing-box --json tagName -q .tagName)
59+
echo "Validating latest upstream cores: xray $xray, sing-box $sb"
60+
fi
61+
echo "xray=$xray" >> "$GITHUB_OUTPUT"
62+
echo "singbox=$sb" >> "$GITHUB_OUTPUT"
63+
64+
- name: Validate generated configs against the cores
65+
id: check
66+
env:
67+
XRAY_VERSION: ${{ steps.versions.outputs.xray }}
68+
SINGBOX_VERSION: ${{ steps.versions.outputs.singbox }}
69+
run: scripts/check-core-compat.sh
70+
71+
# ── Early-warning issue bookkeeping (schedule/dispatch only; on a PR the red
72+
# check is the signal). One tracking issue at a time, deduped by a fixed
73+
# title: a persistent drift never opens a second, and a drift that clears
74+
# auto-closes the open one — so at most one open issue, ever. ──
75+
- name: Open the drift issue
76+
if: failure() && github.event_name != 'pull_request' && steps.check.conclusion == 'failure'
77+
env:
78+
GH_TOKEN: ${{ github.token }}
79+
XRAY_VERSION: ${{ steps.versions.outputs.xray }}
80+
SINGBOX_VERSION: ${{ steps.versions.outputs.singbox }}
81+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
82+
run: |
83+
title="Core compatibility: generated configs rejected by a new core"
84+
existing=$(gh issue list --state open --search "in:title \"$title\"" \
85+
--json number -q '.[0].number // empty')
86+
body=$(cat <<EOF
87+
The config-validation harness rejected at least one generated config when run
88+
against the latest upstream cores:
89+
90+
- xray \`$XRAY_VERSION\`
91+
- sing-box \`$SINGBOX_VERSION\`
92+
93+
A core release likely changed its config schema in a way our generators
94+
(\`xray_config.rs\` / \`singbox_config.rs\` / \`core.rs\`) don't yet match. Fix the
95+
generators before the release auto-bumps the pin, or the bump is blocked.
96+
97+
Failing run: $RUN_URL
98+
EOF
99+
)
100+
if [ -n "$existing" ]; then
101+
echo "tracking issue #$existing already open — not opening another"
102+
else
103+
gh issue create --title "$title" --body "$body"
104+
fi
105+
106+
- name: Close the drift issue when compatibility is restored
107+
if: success() && github.event_name != 'pull_request'
108+
env:
109+
GH_TOKEN: ${{ github.token }}
110+
run: |
111+
title="Core compatibility: generated configs rejected by a new core"
112+
existing=$(gh issue list --state open --search "in:title \"$title\"" \
113+
--json number -q '.[0].number // empty')
114+
if [ -n "$existing" ]; then
115+
gh issue close "$existing" \
116+
--comment "Latest cores accept all generated configs again — closing."
117+
fi

.github/workflows/release.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,23 @@ jobs:
173173
bun run frontend/scripts/check-i18n.ts
174174
( cd frontend && bunx vitest run )
175175
176+
# ── Block the auto-bump when a new core's config schema drifted from our
177+
# generators. "Sync core pins" above already wrote the new versions into
178+
# core-versions.sh, so this stages those exact cores and runs the
179+
# config-validation harness against them. A rejected config fails the job
180+
# before any version is bumped or tagged — so we never ship a release whose
181+
# pinned core rejects the configs we generate. Only runs when cores moved. ──
182+
- name: Setup Rust (core-compat gate)
183+
if: steps.changes.outputs.skip != 'true' && steps.changes.outputs.bins_changed == 'true'
184+
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
185+
with:
186+
toolchain: stable
187+
targets: x86_64-unknown-linux-gnu
188+
189+
- name: Verify the new cores accept our generated configs
190+
if: steps.changes.outputs.skip != 'true' && steps.changes.outputs.bins_changed == 'true'
191+
run: scripts/check-core-compat.sh
192+
176193
- name: Bump version
177194
if: steps.changes.outputs.skip != 'true'
178195
id: bump

scripts/check-core-compat.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
# ============================================================
3+
# scripts/check-core-compat.sh
4+
# Stage the desktop cores and run the config-validation harness against them, so a
5+
# core version whose config schema drifted from our generators (a config the core
6+
# now rejects) fails loudly. Used two ways:
7+
# - release.yml gates the auto-bump on it: a core bump that needs generator
8+
# changes blocks the release instead of shipping a broken build.
9+
# - core-compat.yml runs it on a schedule against the LATEST upstream cores for
10+
# early warning (opens a tracking issue) before the bump ever happens.
11+
#
12+
# Versions come from scripts/core-versions.sh; override the ones under test with
13+
# the usual env vars, e.g.
14+
# XRAY_VERSION=v26.4.0 SINGBOX_VERSION=v1.14.0 scripts/check-core-compat.sh
15+
#
16+
# Usage:
17+
# scripts/check-core-compat.sh [target-triple] # default: host triple
18+
# ============================================================
19+
set -euo pipefail
20+
21+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
22+
23+
# Stage xray / sing-box / tun2socks / libcronet for the target (honours the
24+
# XRAY_VERSION / SINGBOX_VERSION / TUN2SOCKS_VERSION overrides via core-versions.sh).
25+
"$ROOT/scripts/fetch-cores-desktop.sh" "${1:-}"
26+
27+
# Run the harness with the staged cores present (it auto-detects them under
28+
# src-tauri/binaries and validates every generated config against the real cores).
29+
# A rejected config fails the test — and therefore this script.
30+
cargo test --manifest-path "$ROOT/Cargo.toml" \
31+
-p kasumi-core --test core_validation -- --nocapture

0 commit comments

Comments
 (0)