docs(llmdoc): fix stale Recall SetCommon restriction in pine-cpp-runtime #494
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: CI | |
| on: | |
| push: | |
| branches: [master] | |
| paths-ignore: | |
| - ".claude/**" | |
| - ".githooks/**" | |
| - ".github/workflows/agentic-*.yml" | |
| - "apple_generated/**" | |
| - "blog/**" | |
| - "doc/**" | |
| - "**/.gitignore" | |
| - "AGENTS.md" | |
| - "CLAUDE.md" | |
| - "LICENSE" | |
| - "README.md" | |
| pull_request: | |
| paths-ignore: | |
| - ".claude/**" | |
| - ".githooks/**" | |
| - ".github/workflows/agentic-*.yml" | |
| - "apple_generated/**" | |
| - "blog/**" | |
| - "doc/**" | |
| - "**/.gitignore" | |
| - "AGENTS.md" | |
| - "CLAUDE.md" | |
| - "LICENSE" | |
| - "README.md" | |
| # release.yml invokes this workflow on tag pushes as its CI gate. Direct | |
| # tag push triggers were removed so the same SHA does not run CI twice | |
| # (once on push:tags and once via workflow_call from release.yml). | |
| workflow_call: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} | |
| cancel-in-progress: ${{ github.ref_name != 'master' }} | |
| jobs: | |
| go-lint: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # 双 tag 矩阵守门翻默认闸门 (c)(perf-evolution-roadmap §"决策门槛"): | |
| # default = wangshu(默认 Lua 后端);lua_gopher = gopher-lua opt-in。 | |
| # 任一后端 lint 红即拦截,避免 gopher-lua 在默认 build 不被链入而静默漂移。 | |
| lua_backend: [default, gopher] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - name: gofmt drift check | |
| if: matrix.lua_backend == 'default' | |
| working-directory: pine-go | |
| run: | | |
| out=$(gofmt -l $(git ls-files '*.go')) | |
| if [ -n "$out" ]; then | |
| echo "::error::gofmt diff in:" | |
| echo "$out" | |
| exit 1 | |
| fi | |
| - uses: golangci/golangci-lint-action@v9 | |
| with: | |
| working-directory: pine-go | |
| args: ${{ matrix.lua_backend == 'gopher' && '--build-tags=lua_gopher' || '' }} | |
| apple-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - run: pip install ruff | |
| - run: ruff check apple/ | |
| java-lint: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: pine-java | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: "25" | |
| cache: maven | |
| - run: mvn checkstyle:check -B | |
| go-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - run: make go-cover | |
| - name: Upload Go coverage | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: go-coverage | |
| path: pine-go/coverage.out | |
| retention-days: 30 | |
| # 翻默认闸门 (c)(perf-evolution-roadmap §"决策门槛"):双 tag 全绿。 | |
| # 默认走 wangshu(go-test);此 job 守门 gopher-lua opt-in 后端,只跑直接消费 | |
| # Lua 后端的包(operators/lua + integration),避免重复全量回归。 | |
| go-test-gopher: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: pine-go | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - name: Test under -tags=lua_gopher | |
| run: go test -race -tags=lua_gopher ./operators/lua/... ./integration/... | |
| apple-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - run: pip install pytest pytest-cov | |
| - run: python -m pytest apple/tests/ -v --cov=apple --cov-report=term --cov-report=xml:coverage.xml | |
| - name: Upload Python coverage | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: apple-coverage | |
| path: coverage.xml | |
| retention-days: 30 | |
| java-test: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: pine-java | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: "25" | |
| cache: maven | |
| - run: mvn test -B -q | |
| - name: Upload Java coverage | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: java-coverage | |
| path: pine-java/target/site/jacoco/ | |
| retention-days: 30 | |
| cpp-build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install LuaJIT and build deps | |
| # ci-apt-install.sh retries with a per-attempt timeout so one slow | |
| # mirror rotation doesn't fail the job (#125, #164). cmake / gcc / | |
| # make (build-essential) are preinstalled on the runner image — | |
| # asserted below instead of apt-installing shadowed duplicates. | |
| run: | | |
| bash scripts/ci-apt-install.sh libluajit-5.1-dev libcurl4-openssl-dev | |
| cmake --version | head -1 | |
| - name: Configure pine-cpp | |
| working-directory: pine-cpp | |
| run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release | |
| - name: Build pine-cpp | |
| working-directory: pine-cpp | |
| run: cmake --build build -j"$(nproc)" | |
| - name: Smoke-test CLIs | |
| working-directory: pine-cpp | |
| run: | | |
| SCHEMA_JSON=$(mktemp --suffix=.json) | |
| ./build/pineapple-codegen -schema-json "$SCHEMA_JSON" | |
| head -c 200 "$SCHEMA_JSON" && echo | |
| MINI_CFG=$(mktemp --suffix=.json) | |
| python3 -c "import json; json.dump(json.load(open('../fixtures/pipelines/transform_then_filter.json')).get('config', {}), open('$MINI_CFG','w'))" | |
| ./build/pineapple-server -addr ":18999" -config "$MINI_CFG" & | |
| SERVER_PID=$! | |
| for i in 1 2 3 4 5 6 7 8 9 10; do | |
| if curl -fsS http://localhost:18999/health >/dev/null 2>&1; then break; fi | |
| sleep 0.3 | |
| done | |
| curl -fsS http://localhost:18999/health || (kill $SERVER_PID; exit 1) | |
| # P2-04: graceful kill — SIGTERM first so graceful_drain runs to | |
| # completion (in-flight requests finish, ~Server() runs the | |
| # destructors in order); fall back to SIGKILL if drain takes | |
| # too long. Wait is bounded so the step does not hang on a | |
| # stuck handler. | |
| kill -TERM $SERVER_PID 2>/dev/null || true | |
| for i in 1 2 3 4 5; do | |
| kill -0 $SERVER_PID 2>/dev/null || break | |
| sleep 1 | |
| done | |
| kill -KILL $SERVER_PID 2>/dev/null || true | |
| wait $SERVER_PID 2>/dev/null || true | |
| cpp-sanitizer: | |
| runs-on: ubuntu-latest | |
| needs: cpp-build | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Install LuaJIT and build deps | |
| # Resilient apt install — see cpp-build job comment (#125, #164). | |
| run: | | |
| bash scripts/ci-apt-install.sh libluajit-5.1-dev libcurl4-openssl-dev | |
| cmake --version | head -1 | |
| - name: Run ASan/UBSan smoke | |
| run: bash scripts/cpp-sanitizer-smoke.sh | |
| cpp-tsan: | |
| runs-on: ubuntu-latest | |
| needs: cpp-build | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Install LuaJIT and build deps | |
| # Resilient apt install — see cpp-build job comment (#125, #164). | |
| run: | | |
| bash scripts/ci-apt-install.sh libluajit-5.1-dev libcurl4-openssl-dev util-linux | |
| cmake --version | head -1 | |
| - name: Run TSan smoke (high-fanout DAG stress) | |
| run: bash scripts/cpp-tsan-smoke.sh | |
| cpp-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install build deps | |
| # Resilient apt install — see cpp-build job comment (#125, #164). | |
| run: | | |
| bash scripts/ci-apt-install.sh libluajit-5.1-dev libcurl4-openssl-dev | |
| cmake --version | head -1 | |
| - name: Strict build (-Werror) | |
| working-directory: pine-cpp | |
| run: | | |
| cmake -S . -B build-strict -DCMAKE_BUILD_TYPE=Release -DPINE_CPP_WERROR=ON | |
| cmake --build build-strict -j"$(nproc)" | |
| - name: Whitespace + tab hygiene | |
| run: | | |
| set -e | |
| BAD=0 | |
| while IFS= read -r f; do | |
| if grep -nP "[ \t]+$" "$f" >/dev/null 2>&1; then | |
| echo "trailing whitespace in $f:" | |
| grep -nP "[ \t]+$" "$f" | head -5 | |
| BAD=1 | |
| fi | |
| if grep -nP "\t" "$f" >/dev/null 2>&1; then | |
| echo "tab character in $f:" | |
| grep -nP "\t" "$f" | head -5 | |
| BAD=1 | |
| fi | |
| # P2-16: trailing newline guard. Files lacking a final \n | |
| # produced silent diffs when subsequent commits appended | |
| # to them; cpp-lint now rejects the shape outright. | |
| if [[ $(tail -c1 "$f" | wc -l) -eq 0 ]]; then | |
| echo "missing trailing newline in $f" | |
| BAD=1 | |
| fi | |
| done < <(find pine-cpp -type f \( -name '*.cpp' -o -name '*.hpp' \) -not -path '*/build*') | |
| if [[ $BAD -ne 0 ]]; then | |
| echo "cpp-lint: hygiene failures detected (see above)" | |
| exit 1 | |
| fi | |
| - name: Adjacent string-literal concat with bare identifier (typo guard) | |
| run: | | |
| # Catches `"... "" + var + ""` patterns where the author meant | |
| # to embed an identifier inside double quotes but the C++ compiler | |
| # silently concatenates adjacent string literals, dropping the | |
| # intended quotes. Tracked under .code-review/from-v0.8.0 as | |
| # P0-1 — five rounds of code review caught a single instance | |
| # (config.cpp:191) that hid via this exact shape. | |
| if grep -rEn '""[[:space:]]*\+[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*\+[[:space:]]*""' \ | |
| pine-cpp/src pine-cpp/include pine-cpp/cmd pine-cpp/operators; then | |
| echo "cpp-lint: suspicious adjacent-literal concat with bare identifier" >&2 | |
| echo " (likely missing \\\" around an embedded variable; see P0-1)" >&2 | |
| exit 1 | |
| fi | |
| cpp-test: | |
| runs-on: ubuntu-latest | |
| needs: cpp-build | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install build deps | |
| # Resilient apt install — see cpp-build job comment (#125, #164). | |
| run: | | |
| bash scripts/ci-apt-install.sh libluajit-5.1-dev libcurl4-openssl-dev | |
| cmake --version | head -1 | |
| - name: Build + run tests | |
| run: make cpp-test PARALLEL=$(nproc) | |
| java-benchmark: | |
| runs-on: ubuntu-latest | |
| needs: java-test | |
| defaults: | |
| run: | |
| working-directory: pine-java | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: "25" | |
| - name: Run benchmarks | |
| run: mvn test -B -Dtest=BenchmarkTest -pl . 2>&1 | tee benchmark-java.txt | |
| - name: Write benchmark summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| { | |
| echo "### Pine-Java Benchmark results" | |
| echo | |
| echo '```text' | |
| grep -A 100 "=== Pine-Java Pipeline Benchmark ===" benchmark-java.txt || echo "No benchmark output" | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| java-fuzz: | |
| runs-on: ubuntu-latest | |
| needs: java-test | |
| defaults: | |
| run: | |
| working-directory: pine-java | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: "25" | |
| cache: maven | |
| - name: Fuzz Config.load | |
| run: mvn test -B -Dtest="JazzerFuzzTest#fuzzConfigLoad" -Djazzer.instrument="page.liam.pine.**" | |
| env: | |
| JAZZER_FUZZ: "1" | |
| - name: Fuzz DAG.build | |
| run: mvn test -B -Dtest="JazzerFuzzTest#fuzzDAGBuild" -Djazzer.instrument="page.liam.pine.**" | |
| env: | |
| JAZZER_FUZZ: "1" | |
| - name: Fuzz Engine.create | |
| run: mvn test -B -Dtest="JazzerFuzzTest#fuzzEngineCreate" -Djazzer.instrument="page.liam.pine.**" | |
| env: | |
| JAZZER_FUZZ: "1" | |
| cross-validation: | |
| runs-on: ubuntu-latest | |
| needs: [go-test, java-test, cpp-build] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: "25" | |
| cache: maven | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Install redis-server and LuaJIT | |
| # Resilient apt install — see cpp-build job comment (#125, #164). | |
| run: | | |
| bash scripts/ci-apt-install.sh "redis-server=5:7.*" libluajit-5.1-dev libcurl4-openssl-dev | |
| redis-server --version | |
| cmake --version | head -1 | |
| - name: Cross-validate Go vs Java vs C++ | |
| run: make cross-validate | tee cross-validate-output.txt | |
| - name: Fail on any divergence | |
| run: | | |
| if grep -q "^FAIL:" cross-validate-output.txt; then | |
| echo "Cross-validation failures detected:" | |
| grep "^FAIL:" cross-validate-output.txt | |
| exit 1 | |
| fi | |
| - name: Write summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| { | |
| echo "### Cross-Validation Summary" | |
| echo "" | |
| if grep -q "^FAIL:" cross-validate-output.txt 2>/dev/null; then | |
| echo "| Check | Status |" | |
| echo "|-------|--------|" | |
| grep "^PASS:\|^FAIL:" cross-validate-output.txt | while read -r line; do | |
| if [[ "$line" == PASS:* ]]; then | |
| echo "| ${line#PASS: } | ✅ |" | |
| else | |
| echo "| ${line#FAIL: } | ❌ |" | |
| fi | |
| done | |
| elif grep -q "^PASS:" cross-validate-output.txt 2>/dev/null; then | |
| echo "| Check | Status |" | |
| echo "|-------|--------|" | |
| grep "^PASS:" cross-validate-output.txt | while read -r line; do | |
| echo "| ${line#PASS: } | ✅ |" | |
| done | |
| else | |
| echo "Cross-validation did not produce results." | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| codegen-check: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - run: make codegen-check | |
| fuzz: | |
| runs-on: ubuntu-latest | |
| needs: go-test | |
| defaults: | |
| run: | |
| working-directory: pine-go | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - name: Fuzz config.Load | |
| run: go test -run=^$ -fuzz=FuzzLoad -fuzztime=30s -timeout=120s -parallel=4 ./internal/config/ | |
| - name: Fuzz dag.Build | |
| run: go test -run=^$ -fuzz=FuzzBuild -fuzztime=30s -timeout=120s -parallel=4 ./internal/dag/ | |
| - name: Fuzz DataFrame ApplyOutput | |
| run: go test -run=^$ -fuzz=FuzzApplyOutputStorageEquivalence -fuzztime=30s -timeout=120s -parallel=4 ./internal/dataframe/ | |
| - name: Fuzz data_parallel equivalence | |
| run: go test -run=^$ -fuzz=FuzzDataParallelEquivalence -fuzztime=30s -timeout=120s -parallel=4 ./internal/runtime/ | |
| benchmark: | |
| runs-on: ubuntu-latest | |
| needs: go-test | |
| defaults: | |
| run: | |
| working-directory: pine-go | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - name: Run benchmarks | |
| shell: bash | |
| run: | | |
| set -o pipefail | |
| # benchmarks 是独立子 module (pine-go/benchmarks/go.mod),通过 replace | |
| # 指令引用主 module。`make bench` 默认带 -tags=pine_bench 让 stub | |
| # 算子 + BenchmarkCalibrated 都被编译运行(否则 //go:build pine_bench | |
| # 下的源文件在 CI 既不编译也不跑,silent-skip)。 | |
| make bench | tee benchmark.txt | |
| # integration/ 仍在主 module,单独跑(不依赖 pine_bench)。 | |
| go test -bench=. -benchmem -count=1 -run=^$ ./integration/ | tee -a benchmark.txt | |
| - name: Write benchmark summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| { | |
| echo "### Benchmark results" | |
| echo | |
| echo '```text' | |
| if [ -s benchmark.txt ]; then | |
| cat benchmark.txt | |
| else | |
| echo "Benchmark output was not generated." | |
| fi | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Upload benchmark results | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: benchmark-results | |
| path: pine-go/benchmark.txt | |
| retention-days: 30 | |
| differential-fuzz: | |
| runs-on: ubuntu-latest | |
| needs: [go-test, java-test] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: pine-go/go.mod | |
| cache-dependency-path: pine-go/go.sum | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: "25" | |
| cache: maven | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Build Go binary | |
| run: go build -o pineapple-run ./cmd/pineapple-run/ | |
| working-directory: pine-go | |
| - name: Build Java | |
| run: mvn package -B -q -DskipTests | |
| working-directory: pine-java | |
| - name: Install LuaJIT and build deps | |
| # Resilient apt install — see cpp-build job comment (#125, #164). | |
| # libhiredis-dev was dropped: pine-cpp's redis client is raw-socket | |
| # (no hiredis reference anywhere in the tree), and every other cpp | |
| # job already builds without it. | |
| run: | | |
| bash scripts/ci-apt-install.sh libluajit-5.1-dev libcurl4-openssl-dev | |
| cmake --version | head -1 | |
| - name: Build C++ | |
| run: | | |
| cmake -S . -B build -DCMAKE_BUILD_TYPE=Release | |
| cmake --build build -j"$(nproc)" | |
| working-directory: pine-cpp | |
| - name: Run differential fuzz | |
| shell: bash | |
| run: | | |
| SEED=$(date +%s) | |
| echo "Seed: $SEED (use this to reproduce failures)" | |
| python3 scripts/differential-fuzz.py \ | |
| --rounds 100 \ | |
| --stability-runs 2 \ | |
| --seed "$SEED" \ | |
| --engines go,java,cpp \ | |
| --cpp-bin pine-cpp/build/pineapple-run \ | |
| --save-dir /tmp/diff-fuzz-ci | tee diff-fuzz-output.txt | |
| - name: Upload divergences | |
| uses: actions/upload-artifact@v7 | |
| if: failure() | |
| with: | |
| name: differential-fuzz-divergences | |
| path: /tmp/diff-fuzz-ci/ | |
| retention-days: 30 | |
| - name: Write summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| { | |
| echo "### Differential Fuzz" | |
| echo "" | |
| echo '```text' | |
| tail -10 diff-fuzz-output.txt | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" |