fix(sqlite): scan json text columns safely #61
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: uat-bypass | |
| # Phase 47 Plan 03 BYPASS-VERIFY-02/03/04:v3.5 白名单 fail-closed CI 守护。 | |
| # | |
| # 工作流分两层: | |
| # | |
| # 1. lint — 永远跑:bash -n、--help、--dry-run 6 场景全过。 | |
| # 这一步在 ubuntu runner 不需要 nft/docker/sudo,只验证脚本本身。 | |
| # | |
| # 2. uat — 真实 6 场景 × ubuntu-24.04 矩阵。 | |
| # fixture 脚本(scripts/uat-bypass-fixture-up.sh,需要起完整控制面 | |
| # + 颁发 admin token + 起带 sing-box gateway 的 bypass host)在 | |
| # Phase 47 verification 之后才会落地。本 job 通过 preflight step | |
| # 检测 fixture 是否存在: | |
| # - 存在 → 完整跑 6 场景断言 I1–I10。 | |
| # - 缺失 → 显式 SKIP(脚本 exit 2,被 continue-on-error 吞下), | |
| # 仍记录为绿色,但 Summary 里能看到 "fixture missing"。 | |
| # 这样既能在 fixture 合入后 CI 立刻生效守护 ROADMAP SC#5, | |
| # 又不会在 fixture 缺失阶段假阳性阻塞 PR。 | |
| # | |
| # 通过这种分层,PR 阶段能立刻 catch 脚本回归(如 bash 语法、--dry-run 退出码), | |
| # 也能在 fixture 合入后零配置切换到真实 CI 守护。 | |
| on: | |
| pull_request: | |
| paths: | |
| - "internal/network/**" | |
| - "internal/runtime/tasks/**" | |
| - "internal/controlplane/http/admin_bypass*" | |
| - "scripts/uat-bypass.sh" | |
| - ".github/workflows/uat-bypass.yml" | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: uat-bypass-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint: | |
| name: lint + dry-run 6 scenarios | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install minimal tools (jq / curl) | |
| run: | | |
| sudo apt-get update -y | |
| sudo apt-get install -y jq curl | |
| - name: bash -n (syntax) | |
| run: bash -n scripts/uat-bypass.sh | |
| - name: --help contains 6 scenarios + I1..I10 | |
| run: | | |
| set -euo pipefail | |
| OUT=$(bash scripts/uat-bypass.sh --help) | |
| SCEN_COUNT=$(echo "$OUT" | grep -cE 'loopback-only|lan-only|loopback-lan|custom-ip|custom-domain|fail-closed-pkill') | |
| INV_COUNT=$(echo "$OUT" | grep -cE 'I[0-9]') | |
| echo "scenario references: $SCEN_COUNT" | |
| echo "invariant references: $INV_COUNT" | |
| test "$SCEN_COUNT" -ge 6 | |
| test "$INV_COUNT" -ge 10 | |
| - name: --dry-run all 6 scenarios | |
| run: | | |
| set -euo pipefail | |
| for s in loopback-only lan-only loopback-lan custom-ip custom-domain fail-closed-pkill; do | |
| echo "=== dry-run scenario=$s ===" | |
| bash scripts/uat-bypass.sh \ | |
| --scenario="$s" \ | |
| --target-host-id=ci-dryrun \ | |
| --admin-token=ci-dryrun \ | |
| --dry-run | |
| done | |
| uat: | |
| name: uat scenario=${{ matrix.scenario }} | |
| runs-on: ubuntu-24.04 | |
| needs: lint | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| scenario: | |
| - loopback-only | |
| - lan-only | |
| - loopback-lan | |
| - custom-ip | |
| - custom-domain | |
| - fail-closed-pkill | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Preflight — check fixture script availability | |
| id: preflight | |
| run: | | |
| if [ -f scripts/uat-bypass-fixture-up.sh ]; then | |
| echo "fixture_available=true" >> "$GITHUB_OUTPUT" | |
| echo "[INFO] scripts/uat-bypass-fixture-up.sh 存在,将跑完整 uat 流程" | |
| else | |
| echo "fixture_available=false" >> "$GITHUB_OUTPUT" | |
| echo "::warning::scripts/uat-bypass-fixture-up.sh 缺失,本次 uat job 仅 lint job 已守护脚本回归;完整 6 场景断言留待 fixture 落地" | |
| fi | |
| - name: Install prerequisites | |
| if: steps.preflight.outputs.fixture_available == 'true' | |
| run: | | |
| sudo apt-get update -y | |
| sudo apt-get install -y nftables tcpdump jq dnsutils curl | |
| - name: Setup Go | |
| if: steps.preflight.outputs.fixture_available == 'true' | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Build control plane | |
| if: steps.preflight.outputs.fixture_available == 'true' | |
| run: go build -o /tmp/cloud-cli-proxy ./cmd/control-plane | |
| - name: Bring up bypass fixture | |
| if: steps.preflight.outputs.fixture_available == 'true' | |
| run: | | |
| # scripts/uat-bypass-fixture-up.sh 必须输出 /tmp/uat-fixture.json | |
| # 含 host_id + admin_token 两个字段(fixture 契约)。 | |
| bash scripts/uat-bypass-fixture-up.sh | |
| echo "HOST_ID=$(jq -r '.host_id' /tmp/uat-fixture.json)" >> "$GITHUB_ENV" | |
| echo "ADMIN_TOKEN=$(jq -r '.admin_token' /tmp/uat-fixture.json)" >> "$GITHUB_ENV" | |
| - name: Run uat-bypass scenario=${{ matrix.scenario }} | |
| if: steps.preflight.outputs.fixture_available == 'true' | |
| env: | |
| CLOUD_CLI_PROXY_ADMIN_TOKEN: ${{ env.ADMIN_TOKEN }} | |
| CLOUD_CLI_PROXY_API_BASE: http://localhost:8080 | |
| run: | | |
| bash scripts/uat-bypass.sh \ | |
| --scenario=${{ matrix.scenario }} \ | |
| --target-host-id="$HOST_ID" | |
| - name: Teardown fixture | |
| if: always() && steps.preflight.outputs.fixture_available == 'true' | |
| run: | | |
| if [ -f scripts/uat-bypass-fixture-down.sh ]; then | |
| bash scripts/uat-bypass-fixture-down.sh | |
| else | |
| bash scripts/test-fixture-down.sh || true | |
| fi |