fix(copilot): unify payload format and endpoint in complete() #12
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 | ||
| env: | ||
| JCODE_CI: "1" | ||
| on: | ||
| push: | ||
| branches: [main, master] | ||
| pull_request: | ||
| branches: [main, master] | ||
| concurrency: | ||
| group: ci-${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
| env: | ||
| CARGO_TERM_COLOR: always | ||
| SCCACHE_GHA_ENABLED: "true" | ||
| jobs: | ||
| quality: | ||
| name: Quality Guardrails | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 45 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ssh-key: ${{ secrets.DEPLOY_KEY }} | ||
| submodules: recursive | ||
| - name: Configure SSH for cargo git dependencies | ||
| uses: webfactory/ssh-agent@v0.9.0 | ||
| with: | ||
| ssh-private-key: ${{ secrets.DEPLOY_KEY }} | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| components: clippy, rustfmt | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| key: quality-ubuntu | ||
| cache-all-crates: "true" | ||
| - name: Install system dependencies | ||
| # jcode-desktop2 pulls yeslogic-fontconfig-sys, whose build script hard | ||
| # fails without fontconfig.pc. Without this the "Enforce desktop2 frame | ||
| # budget" step cannot even build, and `cargo check --all-targets | ||
| # --all-features` above is affected the same way. | ||
| run: sudo apt-get update && sudo apt-get install -y -qq libfontconfig1-dev | ||
| - name: Check module declarations resolve | ||
| # A `mod x;` with no file makes rustfmt fail with "Error writing files: | ||
| # failed to resolve mod", which reads like a formatting problem and hides | ||
| # every gate behind it. Naming the real cause first (221159294). | ||
| run: python3 scripts/check_module_files.py | ||
| - name: Check formatting | ||
| run: cargo fmt --all -- --check | ||
| - name: Check all targets and all features | ||
| run: cargo check --all-targets --all-features | ||
| - name: Run clippy with warnings denied | ||
| run: cargo clippy --all-targets --all-features -- -D warnings | ||
| - name: Enforce Cargo.lock is up to date | ||
| shell: bash | ||
| # Adding a dependency without regenerating Cargo.lock breaks every | ||
| # `--locked` build. Only the Windows jobs pass `--locked`, so such a | ||
| # commit passes 8 of 9 CI jobs and fails Windows at "Build release | ||
| # binary", skipping all of its validation steps. This catches it in | ||
| # seconds, on the job that already owns dependency hygiene. | ||
| run: cargo metadata --locked --format-version 1 > /dev/null | ||
| - name: Enforce warning budget | ||
| shell: bash | ||
| run: scripts/check_warning_budget.sh | ||
| - name: Enforce oversized-file ratchet | ||
| shell: bash | ||
| run: python3 scripts/check_code_size_budget.py | ||
| - name: Enforce oversized-test ratchet | ||
| shell: bash | ||
| run: python3 scripts/check_test_size_budget.py | ||
| - name: Enforce panic-prone usage ratchet | ||
| shell: bash | ||
| run: python3 scripts/check_panic_budget.py | ||
| - name: Enforce swallowed-error usage ratchet | ||
| shell: bash | ||
| run: python3 scripts/check_swallowed_error_budget.py | ||
| - name: Enforce crate dependency boundaries | ||
| shell: bash | ||
| run: python3 scripts/check_dependency_boundaries.py | ||
| - name: Enforce Rust and TypeScript SDK surface parity | ||
| shell: bash | ||
| run: cargo test -p jcode-sdk parity -- --nocapture | ||
| - name: Enforce wildcard re-export ratchet | ||
| shell: bash | ||
| run: python3 scripts/check_wildcard_reexport_budget.py | ||
| # Frame-cost gate for desktop2. `build_scene` is a pure function of its | ||
| # model and `states::NODES` enumerates the app's visual states, so frame | ||
| # cost is something CI can evaluate rather than something a person has to | ||
| # notice by using the app and reporting that it feels slow. | ||
| # | ||
| # The decisive assertion is about work, not time: an unchanged frame must | ||
| # not lay the transcript out again. That is exact, so it fires on a | ||
| # contended CI runner exactly as it does locally, unlike a wall-clock | ||
| # budget which has to carry enough slack to hide real regressions. | ||
| # | ||
| # Runs on the default profile so it reuses this job's existing build | ||
| # artifacts rather than compiling a second dependency tree. | ||
| - name: Enforce desktop2 frame budget | ||
| shell: bash | ||
| run: "cargo test -p jcode-desktop2 profile:: -- --test-threads=1" | ||
| - name: Enforce no unused dependencies | ||
| shell: bash | ||
| run: | | ||
| cargo install cargo-machete --locked | ||
| cargo machete | ||
| release-automation: | ||
| name: Release Automation | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Test Discord release announcements | ||
| run: python3 -m unittest -v scripts/test_post_discord_release.py | ||
| - name: Compile release automation scripts | ||
| run: python3 -m py_compile scripts/post_discord_release.py scripts/test_post_discord_release.py | ||
| build: | ||
| name: Build & Test (${{ matrix.os }}) | ||
| runs-on: ${{ matrix.os }} | ||
| # 35 minutes covers a normal push (~12 min with a warm Swatinem cache) but | ||
| # not a *rerun*, where the cache is cold: three consecutive reruns of a | ||
| # known-good commit were cancelled at the cap, each getting further through | ||
| # the same green steps (#693). A rerun is exactly what you reach for after a | ||
| # concurrency cancellation, so the old cap made healthy commits look red. | ||
| # 75 still bounds a genuinely hung job well under the windows job's 150. | ||
| timeout-minutes: 75 | ||
| # Some dependencies (e.g. convert_case 0.10.0 via derive_more/crossterm, and | ||
| # proc-macro2/quote) accidentally ship a `rust-toolchain.toml` *inside their | ||
| # published crate*. When cargo builds such a crate its CWD is that crate dir, | ||
| # so the rustup proxy honours the file: convert_case pins `channel = "1.83.0"` | ||
| # and proc-macro2 requests `components = ["rust-src"]`. That made every | ||
| # Build & Test job (a) race two parallel `rust-src` downloads and (b) later | ||
| # try to compile convert_case with rustc 1.83.0, failing with E0514/E0599. | ||
| # RUSTUP_TOOLCHAIN takes precedence over any rust-toolchain.toml override, so | ||
| # pinning it here makes every step use this job's installed stable toolchain. | ||
| env: | ||
| RUSTUP_TOOLCHAIN: stable | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| os: [ubuntu-latest, macos-latest] | ||
| include: | ||
| - os: ubuntu-latest | ||
| target: x86_64-unknown-linux-gnu | ||
| - os: macos-latest | ||
| target: aarch64-apple-darwin | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ssh-key: ${{ secrets.DEPLOY_KEY }} | ||
| submodules: recursive | ||
| - name: Configure SSH for cargo git dependencies | ||
| uses: webfactory/ssh-agent@v0.9.0 | ||
| with: | ||
| ssh-private-key: ${{ secrets.DEPLOY_KEY }} | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| targets: ${{ matrix.target }} | ||
| # Pre-install rust-src in the (serial) toolchain step. Some build-script | ||
| # / proc-macro units trigger an on-demand `rustup component add rust-src`, | ||
| # and when several parallel cargo/rustc processes request it at once they | ||
| # race on the shared `~/.rustup/downloads/*.partial` file and fail with | ||
| # "could not rename ... No such file or directory". Fetching it once here | ||
| # removes the race. (Was failing every Build & Test job.) | ||
| components: rust-src | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| # Suffix bumped to evict caches that held 1.83.0 / <unknown rustc> | ||
| # artifacts produced before RUSTUP_TOOLCHAIN=stable pinned the job. | ||
| key: ${{ matrix.os }}-stablepin | ||
| - name: Install mold linker (Linux) | ||
| if: runner.os == 'Linux' | ||
| run: | | ||
| sudo apt-get update -qq | ||
| sudo apt-get install -y -qq mold | ||
| - name: Build | ||
| shell: bash | ||
| run: | | ||
| mkdir -p .cargo | ||
| if [ "$RUNNER_OS" = "Linux" ]; then | ||
| cat > .cargo/config.toml << 'EOF' | ||
| [target.x86_64-unknown-linux-gnu] | ||
| linker = "clang" | ||
| rustflags = ["-C", "link-arg=-fuse-ld=mold"] | ||
| EOF | ||
| fi | ||
| # NOTE: intentionally NOT exporting RUSTC_WRAPPER=sccache here. Only this | ||
| # step used sccache while the later `cargo test --no-run` steps did not, | ||
| # so sccache emitted rlibs/rmetas stamped `<unknown rustc version>` that | ||
| # the non-sccache test compile then rejected with E0514 | ||
| # ("compiled by an incompatible version of rustc"). rust-cache already | ||
| # caches target/ across runs, so a clean, wrapper-consistent build is | ||
| # both correct and fast enough. | ||
| export RUSTC="$(rustup which rustc)" | ||
| CARGO_BIN="$(rustup which cargo)" | ||
| "$CARGO_BIN" build --release --target ${{ matrix.target }} | ||
| - name: Compile library and binary tests | ||
| shell: bash | ||
| run: | | ||
| python3 .github/scripts/run_with_timeout.py 900 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} --lib --bins --no-run | ||
| - name: Run deterministic retention-readiness cohort | ||
| shell: bash | ||
| run: | | ||
| # A cold jcode-app-core test harness compiles heavy optional provider | ||
| # dependencies and can exceed three minutes on hosted runners. The | ||
| # cohort itself runs in under a second once the lib test is built. | ||
| python3 .github/scripts/run_with_timeout.py 600 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} \ | ||
| -p jcode-app-core --lib retention_readiness -- --nocapture | ||
| - name: Run secret-input pty cohort (all platforms with a pty) | ||
| if: runner.os != 'Windows' | ||
| shell: bash | ||
| # `jcode-base --lib` is never executed on Linux CI: the only jcode-base | ||
| # test invocation anywhere in this workflow is a Windows-only | ||
| # `power_inhibit::tests::windows_` filter. So the masking fix for #660 had | ||
| # a test that was compiled and never run, which is the same | ||
| # looks-green-but-never-ran shape as the warning budget and the stdin | ||
| # detector (#651). These tests fork a pty, so they cannot run on the | ||
| # Windows runner; everywhere else they take about a second. | ||
| # | ||
| # 600s, not 300s: the budget is dominated by cold-compiling the | ||
| # jcode-base lib test harness, which exceeded 300s on a hosted macOS | ||
| # runner (run 30591536707) while the cohort itself runs in a second. | ||
| run: | | ||
| python3 .github/scripts/run_with_timeout.py 600 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} \ | ||
| -p jcode-base --lib secret_input -- --nocapture | ||
| - name: Run embedding numeric-stability cohort (Linux only) | ||
| if: runner.os == 'Linux' | ||
| shell: bash | ||
| # `minilm_embedding_is_numerically_stable_across_inference_engines` pins | ||
| # the model's actual output, which is the only thing that can catch an | ||
| # inference-engine upgrade silently changing embeddings: persisted | ||
| # memories are keyed by model_id, which does not change across a tract | ||
| # bump, so the stale-embedding filter cannot notice (#657). | ||
| # | ||
| # That test skips itself when the model is absent, and jcode-embedding | ||
| # tests are not otherwise run here at all, so without this step it would | ||
| # never execute. Fetch the model first (~87MB) so the cohort is real | ||
| # rather than a silent skip. | ||
| run: | | ||
| set -euo pipefail | ||
| model_dir="$HOME/.jcode/models/all-MiniLM-L6-v2" | ||
| mkdir -p "$model_dir" | ||
| base="https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main" | ||
| curl -sSfL --retry 3 -o "$model_dir/model.onnx" "$base/onnx/model.onnx" | ||
| curl -sSfL --retry 3 -o "$model_dir/tokenizer.json" "$base/tokenizer.json" | ||
| # Fail loudly if the fetch produced something unusable, so this cannot | ||
| # degrade back into a silent skip. | ||
| test -s "$model_dir/model.onnx" | ||
| test -s "$model_dir/tokenizer.json" | ||
| python3 .github/scripts/run_with_timeout.py 600 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} \ | ||
| -p jcode-embedding --lib -- --nocapture \ | ||
| | tee /tmp/embedding-cohort.log | ||
| # The test is present and the harness ran it (rather than the binary | ||
| # failing to build). | ||
| grep -q "minilm_embedding_is_numerically_stable_across_inference_engines ... ok" \ | ||
| /tmp/embedding-cohort.log | ||
| # This is the check that matters. A skipped test still reports "ok", | ||
| # so the reported result alone cannot distinguish "verified" from | ||
| # "silently did nothing"; only the skip message can. | ||
| if grep -q "skip: MiniLM model not present" /tmp/embedding-cohort.log; then | ||
| echo "::error::embedding cohort skipped despite fetching the model" | ||
| exit 1 | ||
| fi | ||
| - name: Run stdin-forwarding cohort (all platforms) | ||
| shell: bash | ||
| # These tests exercise the per-platform stdin detector, whose | ||
| # implementations are entirely separate (`/proc/PID/syscall` on Linux, | ||
| # `proc_pidinfo` + thread state on macOS). They were never gated: the | ||
| # only `jcode-app-core --lib` run above is filtered to | ||
| # `retention_readiness`, and the broader suite is Linux-only, so the | ||
| # macOS detector had no coverage at all. `TH_STATE_WAITING` was defined | ||
| # as 2 (`TH_STATE_STOPPED`) rather than 3, which silently disabled | ||
| # macOS stdin forwarding entirely until #651. | ||
| # | ||
| # Deliberately not Linux-gated: running this on both Unix platforms is | ||
| # the whole point. Windows is a separate job and is not covered here, | ||
| # because these tests drive `head -n1`, which stock Windows does not | ||
| # provide; the Windows detector still has no test coverage. | ||
| run: | | ||
| python3 .github/scripts/run_with_timeout.py 600 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} \ | ||
| -p jcode-app-core --lib tool::bash::tests::test_stdin_forwarding -- --nocapture | ||
| - name: Run TUI library tests (Linux only) | ||
| if: runner.os == 'Linux' | ||
| shell: bash | ||
| # These were previously only compiled (`--no-run` above), so test-only | ||
| # breakage reached master with CI green: three `ServerEvent::MessageEnd` | ||
| # call sites stayed unit variants after the enum gained a field, and the | ||
| # whole target failed to compile unnoticed (see #592). | ||
| # | ||
| # Run serially. Many of these tests share process-global state (model | ||
| # catalog, ambient cache, render state) and fail on ordering under | ||
| # parallelism, while passing reliably one-at-a-time: 1977 pass serially | ||
| # versus 2-4 varying failures in parallel. Scoping that state is tracked | ||
| # in #592; until then serial execution is the honest signal, and costs | ||
| # ~30s once the target is already built above. | ||
| # | ||
| # Two render tests depend on terminal color support and are still | ||
| # skipped. The copy-badge cohort no longer is: every one of those tests | ||
| # now installs the in-process `CapturedClipboard` sink and asserts the | ||
| # copied text through it, so they never touch the OS clipboard and pass | ||
| # on a headless runner (verified with DISPLAY/WAYLAND_DISPLAY unset). | ||
| # They were the tests most worth running, since they cover the copy path | ||
| # end to end. Tracked in #592. | ||
| # | ||
| # COLORTERM: the runner's headless shell advertises no color support, | ||
| # so capability detection falls back to 256-color and every RGB cell | ||
| # quantizes to `Indexed`. The palette-topology measurement identifies | ||
| # roles by their rendered RGB, and quantization error pushes some roles | ||
| # outside the matcher's family radius ("got 4 edges", run 30617961697). | ||
| # Declaring truecolor tests the code path users overwhelmingly run. | ||
| env: | ||
| COLORTERM: truecolor | ||
| run: | | ||
| python3 .github/scripts/run_with_timeout.py 600 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} \ | ||
| -p jcode-tui --lib -- --test-threads=1 \ | ||
| --skip test_prompt_entry_shimmer_color_moves_across_positions \ | ||
| --skip right_fact_stack_uses_neutral_gray_except_for_context_usage | ||
| - name: Compile integration test binaries | ||
| shell: bash | ||
| # Build the integration-test binaries up front (not counted against the | ||
| # run steps' execution budgets). The e2e suite in particular is heavy | ||
| # (burst-spawn concurrency), and folding compilation into its 900s run | ||
| # budget was pushing slower hosted ubuntu/macos runners over the limit. | ||
| run: | | ||
| python3 .github/scripts/run_with_timeout.py 900 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} \ | ||
| --test provider_matrix --test e2e --no-run | ||
| - name: Run provider matrix tests | ||
| shell: bash | ||
| run: | | ||
| python3 .github/scripts/run_with_timeout.py 600 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} --test provider_matrix | ||
| - name: Run e2e tests | ||
| shell: bash | ||
| run: | | ||
| # e2e is fast (~1-2 min of execution); the generous budget only guards | ||
| # against an occasional slow hosted runner. Compilation is already done | ||
| # in the dedicated compile step above. | ||
| python3 .github/scripts/run_with_timeout.py 600 \ | ||
| "$(rustup which cargo)" test --target ${{ matrix.target }} --test e2e | ||
| - name: Check PowerShell script syntax (Windows) | ||
| if: runner.os == 'Windows' | ||
| shell: pwsh | ||
| run: | | ||
| ./scripts/check_powershell_syntax.ps1 | ||
| - name: Enforce warning budget (Linux) | ||
| if: runner.os == 'Linux' | ||
| shell: bash | ||
| run: | | ||
| scripts/check_warning_budget.sh | ||
| - name: Security preflight (Linux) | ||
| if: runner.os == 'Linux' | ||
| shell: bash | ||
| run: | | ||
| cargo install cargo-audit --locked | ||
| scripts/security_preflight.sh --strict | ||
| windows-build-test: | ||
| name: Build & Test (windows-latest) | ||
| runs-on: windows-latest | ||
| timeout-minutes: 150 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ssh-key: ${{ secrets.DEPLOY_KEY }} | ||
| submodules: recursive | ||
| - name: Configure SSH for cargo git dependencies | ||
| uses: webfactory/ssh-agent@v0.9.0 | ||
| with: | ||
| ssh-private-key: ${{ secrets.DEPLOY_KEY }} | ||
| - uses: ilammy/msvc-dev-cmd@v1 | ||
| with: | ||
| arch: amd64 | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| targets: x86_64-pc-windows-msvc | ||
| - name: Setup sccache | ||
| uses: mozilla-actions/sccache-action@v0.0.7 | ||
| continue-on-error: true | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| key: windows-latest | ||
| cache-all-crates: "true" | ||
| - name: Build release binary | ||
| shell: pwsh | ||
| run: | | ||
| if (Get-Command sccache -ErrorAction SilentlyContinue) { | ||
| sccache --start-server *> $null | ||
| if ($LASTEXITCODE -eq 0) { | ||
| $env:RUSTC_WRAPPER = 'sccache' | ||
| } | ||
| } | ||
| & cargo build --locked --release --target x86_64-pc-windows-msvc | ||
| if ($LASTEXITCODE -ne 0) { | ||
| throw 'Windows release build failed' | ||
| } | ||
| - name: Compile library and binary tests | ||
| shell: pwsh | ||
| run: | | ||
| & cargo test --locked --target x86_64-pc-windows-msvc --lib --bins --no-run | ||
| if ($LASTEXITCODE -ne 0) { | ||
| throw 'Windows library/binary test compilation failed' | ||
| } | ||
| - name: Run targeted Windows validation tests | ||
| shell: pwsh | ||
| run: | | ||
| $tests = @( | ||
| 'command_candidates_adds_extension_on_windows', | ||
| 'command_exists_for_known_binary', | ||
| 'command_exists_absolute_path', | ||
| 'sibling_socket_path_roundtrip', | ||
| 'cleanup_socket_pair_removes_main_and_debug_files', | ||
| 'is_process_running_reports_exited_children_as_stopped', | ||
| 'spawn_replacement_process_returns_without_waiting_for_child_exit', | ||
| 'build_shell_command_uses_cmd_and_executes_command', | ||
| 'pipe_name_is_stable_and_normalizes_case_and_separators', | ||
| 'pipe_name_falls_back_when_stem_is_empty', | ||
| 'busy_pipe_is_reported_as_a_live_socket_path', | ||
| 'stream_pair_round_trips_bytes', | ||
| 'split_stream_supports_concurrent_read_and_write', | ||
| 'test_cancel_command_idle_reports_nothing_to_cancel', | ||
| 'test_menu_number_rejected_as_api_key', | ||
| 'test_command_palette_suppressed_while_api_key_prompt_pending', | ||
| 'test_ctrl_c_with_active_copy_selection_copies_instead_of_quitting', | ||
| 'test_ctrl_c_in_copy_mode_without_selection_still_falls_through' | ||
| ) | ||
| foreach ($testName in $tests) { | ||
| & ./scripts/invoke_cargo_with_timeout.ps1 ` | ||
| -Name "Windows targeted test: $testName" ` | ||
| -TimeoutSeconds 300 ` | ||
| -CargoArgs @('test', '--locked', '--target', 'x86_64-pc-windows-msvc', '--lib', $testName, '--', '--nocapture') | ||
| } | ||
| - name: Run Windows hotkey and power tests | ||
| shell: pwsh | ||
| run: | | ||
| & ./scripts/invoke_cargo_with_timeout.ps1 ` | ||
| -Name 'Windows setup-hints tests' ` | ||
| -TimeoutSeconds 300 ` | ||
| -CargoArgs @('test', '--locked', '--target', 'x86_64-pc-windows-msvc', '-p', 'jcode-setup-hints', '--lib', '--', '--nocapture') | ||
| & ./scripts/invoke_cargo_with_timeout.ps1 ` | ||
| -Name 'Windows power-inhibit tests' ` | ||
| -TimeoutSeconds 900 ` | ||
| -CargoArgs @('test', '--locked', '--target', 'x86_64-pc-windows-msvc', '-p', 'jcode-base', '--no-default-features', '--lib', 'power_inhibit::tests::windows_', '--', '--nocapture') | ||
| - name: Run Windows e2e smoke tests | ||
| shell: pwsh | ||
| run: | | ||
| $tests = @( | ||
| 'provider_behavior::test_socket_model_cycle_supported_models', | ||
| 'provider_behavior::test_model_switch_resets_provider_session' | ||
| ) | ||
| foreach ($testName in $tests) { | ||
| & ./scripts/invoke_cargo_with_timeout.ps1 ` | ||
| -Name "Windows e2e smoke test: $testName" ` | ||
| -TimeoutSeconds 420 ` | ||
| -CargoArgs @('test', '--locked', '--target', 'x86_64-pc-windows-msvc', '--test', 'e2e', $testName, '--', '--exact', '--nocapture') | ||
| } | ||
| - name: Run Windows lifecycle e2e tests | ||
| shell: pwsh | ||
| env: | ||
| JCODE_E2E_ARTIFACT_DIR: ${{ runner.temp }}/jcode-windows-e2e-logs | ||
| run: | | ||
| New-Item -ItemType Directory -Force -Path $env:JCODE_E2E_ARTIFACT_DIR | Out-Null | ||
| $tests = @( | ||
| 'windows_lifecycle::windows_binary_server_accepts_clients_and_debug_cli', | ||
| 'windows_lifecycle::windows_binary_server_rebinds_named_pipe_after_exit' | ||
| ) | ||
| foreach ($testName in $tests) { | ||
| & ./scripts/invoke_cargo_with_timeout.ps1 ` | ||
| -Name "Windows lifecycle e2e test: $testName" ` | ||
| -TimeoutSeconds 420 ` | ||
| -CargoArgs @('test', '--locked', '--target', 'x86_64-pc-windows-msvc', '--test', 'e2e', $testName, '--', '--exact', '--nocapture') | ||
| } | ||
| - name: Upload Windows e2e diagnostics | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: windows-e2e-diagnostics | ||
| path: ${{ runner.temp }}/jcode-windows-e2e-logs | ||
| if-no-files-found: ignore | ||
| - name: Verify built binary launches | ||
| shell: pwsh | ||
| run: | | ||
| & "target/x86_64-pc-windows-msvc/release/jcode.exe" --version | ||
| if ($LASTEXITCODE -ne 0) { | ||
| throw 'Built Windows binary failed to run --version' | ||
| } | ||
| - name: Run Windows installer lifecycle tests | ||
| shell: pwsh | ||
| run: | | ||
| & ./scripts/test_windows_launcher_install.ps1 | ||
| & ./scripts/test_windows_setup_evaluation.ps1 | ||
| - name: Verify installer using local artifact | ||
| shell: pwsh | ||
| run: | | ||
| $cargoVersion = Select-String -Path Cargo.toml -Pattern '^version\s*=\s*"([^"]+)"' | Select-Object -First 1 | ||
| if (-not $cargoVersion) { | ||
| throw 'Could not determine Cargo.toml version' | ||
| } | ||
| # CI builds are development builds. Build metadata independently derives | ||
| # this identity from the root package version and appends `-dev`. | ||
| $version = 'v' + $cargoVersion.Matches[0].Groups[1].Value + '-dev' | ||
| & ./.github/scripts/verify_windows_install.ps1 ` | ||
| -ArtifactExePath 'target/x86_64-pc-windows-msvc/release/jcode.exe' ` | ||
| -Version $version | ||
| fmt: | ||
| name: Format | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| components: rustfmt | ||
| - name: Check module declarations resolve | ||
| # A `mod x;` with no file makes rustfmt fail with "Error writing files: | ||
| # failed to resolve mod", which reads like a formatting problem and hides | ||
| # every gate behind it. Naming the real cause first (221159294). | ||
| run: python3 scripts/check_module_files.py | ||
| - name: Check formatting | ||
| run: cargo fmt --all -- --check | ||
| typescript-sdk: | ||
| name: TypeScript SDK | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "22" | ||
| cache: npm | ||
| cache-dependency-path: sdk/typescript/package-lock.json | ||
| # The SDK mirrors crates/jcode-harness-api by hand. Without this job the | ||
| # only guard that runs on a change is the Rust-side variant check, and a | ||
| # typo in the TypeScript itself (or a broken client) reaches consumers. | ||
| - name: Install | ||
| run: npm ci --no-audit --no-fund | ||
| working-directory: sdk/typescript | ||
| - name: Typecheck, build, and test | ||
| run: npm run check | ||
| working-directory: sdk/typescript | ||
| # `npm run check` proves the source compiles; it says nothing about what | ||
| # a consumer actually receives. The published tarball is a separate | ||
| # artifact (`files`, `exports`, `prepack`), and getting it wrong ships a | ||
| # package that installs but cannot be imported. Install it as a real | ||
| # dependency and import it the way a consumer would. | ||
| - name: Published tarball installs and imports | ||
| run: bash scripts/test_sdk_package.sh | ||
| setup-friction: | ||
| name: Setup Friction Eval (Linux installer) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Install probe shells | ||
| run: sudo apt-get update && sudo apt-get install -y fish zsh | ||
| - name: Installer conversion telemetry tests | ||
| run: bash scripts/test_install_conversion.sh | ||
| - name: Setup friction scorecard | ||
| run: bash scripts/setup_friction_eval.sh | ||
| powershell-syntax: | ||
| name: PowerShell Syntax | ||
| runs-on: windows-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Check PowerShell script syntax (Windows PowerShell 5.1) | ||
| shell: powershell | ||
| run: | | ||
| & ./scripts/check_powershell_syntax.ps1 | ||
| - name: Check PowerShell script syntax (PowerShell 7) | ||
| shell: pwsh | ||
| run: | | ||
| & ./scripts/check_powershell_syntax.ps1 | ||
| windows-cross-check: | ||
| name: Windows Cross-Target Check (Linux) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 35 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ssh-key: ${{ secrets.DEPLOY_KEY }} | ||
| submodules: recursive | ||
| - name: Configure SSH for cargo git dependencies | ||
| uses: webfactory/ssh-agent@v0.9.0 | ||
| with: | ||
| ssh-private-key: ${{ secrets.DEPLOY_KEY }} | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| targets: x86_64-pc-windows-msvc,aarch64-pc-windows-msvc | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| key: windows-cross-check | ||
| cache-all-crates: "true" | ||
| - name: Install LLVM toolchain for cargo-xwin | ||
| run: | | ||
| sudo apt-get update -qq | ||
| sudo apt-get install -y -qq clang lld llvm ninja-build | ||
| - name: Install cargo-xwin | ||
| run: cargo install --git https://github.com/rust-cross/cargo-xwin cargo-xwin | ||
| - name: Check Windows x64 target | ||
| run: cargo xwin check --locked --target x86_64-pc-windows-msvc | ||
| # cargo-xwin currently feeds clang-style ring builds MSVC /imsvc flags for | ||
| # aarch64-pc-windows-msvc on Linux. Keep this advisory until upstream | ||
| # cargo-xwin/ring interop is fixed; native Windows ARM64 smoke covers the | ||
| # release artifact path. | ||
| - name: Check Windows ARM64 target (advisory) | ||
| continue-on-error: true | ||
| run: cargo xwin check --locked --target aarch64-pc-windows-msvc --no-default-features --features pdf | ||