name: shoes schema compatibility
on:
push:
paths:
- "src/**"
- "tests/**"
- "examples/**"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/shoes-schema.yml"
pull_request:
paths:
- "src/**"
- "tests/**"
- "examples/**"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/shoes-schema.yml"
workflow_dispatch:
permissions:
contents: read
env:
SHOES_COMMIT: 386b11532424b8665ee3e46340c6236fb3c47595
SHOES_VERSION: 0.2.8
jobs:
dry-run:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 - name: Build fixed shoes revision
shell: bash
run: |
set -euo pipefail
shoes_source="$RUNNER_TEMP/shoes-source"
shoes_target="$RUNNER_TEMP/shoes-target"
git init "$shoes_source"
git -C "$shoes_source" remote add origin https://github.com/cfal/shoes.git
git -C "$shoes_source" fetch --depth 1 origin "$SHOES_COMMIT"
git -C "$shoes_source" checkout --detach FETCH_HEAD
test "$(git -C "$shoes_source" rev-parse HEAD)" = "$SHOES_COMMIT"
CARGO_TARGET_DIR="$shoes_target" cargo build \
--locked --release --manifest-path "$shoes_source/Cargo.toml"
sudo install -m 0755 "$shoes_target/release/shoes" /usr/local/bin/shoes
shoes_version="$(shoes --version)"
printf '%s\n' "$shoes_version"
grep -F "$SHOES_VERSION" <<<"$shoes_version"
- name: Build ping-rust
run: cargo build --locked
- name: Validate and exercise chain proxy with fixed shoes revision
env:
PING_RUST_SHOES_E2E_BIN: /usr/local/bin/shoes
run: |
shoes --dry-run examples/chain-proxy.yaml
shoes --dry-run examples/chain-e2e-upstream.yaml
shoes --dry-run examples/chain-e2e-downstream.yaml
shoes --dry-run examples/chain-e2e-client.yaml
cargo test --locked --test chain_proxy_e2e -- --nocapture
- name: Generate and validate every protocol
shell: bash
run: |
set -euo pipefail
bin="$PWD/target/debug/ping-rust"
out="$PWD/target/shoes-validation"
mkdir -p "$out"
"$bin" generate reality --output "$out/reality.yaml" \
--port 1443 --server-name www.cloudflare.com \
--dest www.cloudflare.com:443 >/dev/null
"$bin" generate hysteria2 --output "$out/hysteria2.yaml" \
--port 2443 --server-name hy2.example.com --quic-endpoints 1 >/dev/null
"$bin" generate tuic --output "$out/tuic.yaml" \
--port 3443 --server-name tuic.example.com --zero-rtt >/dev/null
"$bin" generate shadowsocks --output "$out/shadowsocks.yaml" \
--port 4388 >/dev/null
"$bin" generate anytls --output "$out/anytls.yaml" \
--port 5443 --server-name anytls.example.com \
--user alice:anytls-test-password \
--padding stop=8 --padding 0=30-30 --padding 1=50-100 \
--fallback 127.0.0.1:80 >/dev/null
"$bin" generate vless-tls --output "$out/vless-tls.yaml" \
--port 6443 --server-name vless-tls.example.com >/dev/null
"$bin" generate vless-ws-tls --output "$out/vless-ws-tls.yaml" \
--port 6444 --server-name vless-ws.example.com \
--websocket-path /vless >/dev/null
"$bin" generate trojan-tls --output "$out/trojan-tls.yaml" \
--port 6445 --server-name trojan.example.com >/dev/null
"$bin" generate trojan-reality --output "$out/trojan-reality.yaml" \
--port 6446 --server-name www.cloudflare.com \
--dest www.cloudflare.com:443 >/dev/null
"$bin" generate vmess-ws-tls --output "$out/vmess-ws-tls.yaml" \
--port 6447 --server-name vmess.example.com \
--websocket-path /vmess >/dev/null
for config in \
reality hysteria2 tuic shadowsocks anytls \
vless-tls vless-ws-tls trojan-tls trojan-reality vmess-ws-tls; do
shoes --dry-run "$out/$config.yaml"
done
cat \
"$out/reality.yaml" \
"$out/hysteria2.yaml" \
"$out/tuic.yaml" \
"$out/shadowsocks.yaml" \
"$out/anytls.yaml" \
"$out/vless-tls.yaml" \
"$out/vless-ws-tls.yaml" \
"$out/trojan-tls.yaml" \
"$out/trojan-reality.yaml" \
"$out/vmess-ws-tls.yaml" >"$out/all-ten.yaml"
shoes --dry-run "$out/all-ten.yaml"
- name: Start every generated listener
shell: bash
run: |
set -euo pipefail
out="$PWD/target/shoes-validation"
shoes "$out/all-ten.yaml" >"$out/runtime.log" 2>&1 &
shoes_pid="$!"
trap 'kill "$shoes_pid" 2>/dev/null || true' EXIT
for _ in {1..40}; do
tcp_ports="$(ss -H -lnt | awk '{print $4}')"
udp_ports="$(ss -H -lun | awk '{print $4}')"
if grep -Eq '(^|:)1443$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)4388$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)5443$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)6443$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)6444$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)6445$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)6446$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)6447$' <<<"$tcp_ports" \
&& grep -Eq '(^|:)2443$' <<<"$udp_ports" \
&& grep -Eq '(^|:)3443$' <<<"$udp_ports"; then
exit 0
fi
sleep 0.25
done
cat "$out/runtime.log" >&2
exit 1
- name: Validate all Shadowsocks ciphers and Reality plus AnyTLS
shell: bash
run: |
set -euo pipefail
bin="$PWD/target/debug/ping-rust"
out="$PWD/target/shoes-validation"
ciphers=(
aes-128-gcm
aes-256-gcm
chacha20-ietf-poly1305
2022-blake3-aes-128-gcm
2022-blake3-aes-256-gcm
2022-blake3-chacha20-ietf-poly1305
)
passwords=(
legacy-aes-128-password
legacy-aes-256-password
legacy-chacha-password
AAAAAAAAAAAAAAAAAAAAAA==
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
)
for index in "${!ciphers[@]}"; do
config="$out/ss-$index.yaml"
"$bin" generate shadowsocks --output "$config" \
--port "$((6000 + index))" \
--cipher "${ciphers[$index]}" \
--password "${passwords[$index]}" >/dev/null
shoes --dry-run "$config"
done
"$bin" generate anytls --output "$out/anytls-reality.yaml" \
--port 7443 --anytls-mode reality \
--server-name www.cloudflare.com \
--dest www.cloudflare.com:443 \
--user advanced:anytls-reality-password >/dev/null
shoes --dry-run "$out/anytls-reality.yaml"