name: CI on: push: branches: [main, master] pull_request: branches: [main, master] # Least privilege: every job here only reads the repository. Without this # block each job inherits the repository default token scope, which is handed # to third-party actions (the dependency-audit job passes it explicitly), so a # compromised action would hold whatever write scopes the default grants. permissions: contents: read env: CARGO_TERM_COLOR: always jobs: # ── Workflow linting (YAML syntax) ──────────────────────────────────── lint-workflows: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Validate YAML syntax run: python3 -c "import yaml; [yaml.safe_load(open(f'.github/workflows/{w}')) for w in ['ci.yml','release.yml','docs.yml']]" # ── Rust tests (core + gdext + cli) ────────────────────────────────── rust-tests: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable - name: Cache cargo uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: | ~/.cargo/registry target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Run clippy run: cargo clippy --workspace -- -D warnings - name: Run Rust tests run: cargo test --workspace -- --test-threads=1 # Serial first (deterministic baseline), then default parallelism: # the second pass is what catches tests that only pass because nothing # else runs concurrently (shared statics, fixed paths). - name: Run Rust tests (parallel) run: cargo test --workspace # The peer-credential code is `cfg(target_os)`-selected, so the BSD arm is # dead code on this runner: this is the only place it gets compiled. # illumos and NetBSD cannot be checked the same way (their bundled SQLite # does not build there); those two arms are probe-verified in the ROADMAP. - name: Cross-check the BSD peer-credential arm run: | rustup target add x86_64-unknown-freebsd cargo check -p gpty-ipc --target x86_64-unknown-freebsd # ── OMP extension tests (Node, no omp install) ──────────────────────── omp-events-tests: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Run @gpty/omp-events tests run: node --test working-directory: extensions/gpty-omp-events # ── Rust Windows compile check (release parity) ─────────────────────── # The release workflow builds gpty-gdext on windows-latest; without this # job, Windows-only compile errors only surface at tag time. rust-windows: runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable - name: Cache cargo uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: | ~/.cargo/registry target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Check Windows build env: RUSTFLAGS: -Dwarnings run: cargo check --workspace # ── GDScript tests (GUT headless) ──────────────────────────────────── gut-tests: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install Godot headless run: | sudo apt-get update -y && sudo apt-get install -y wget unzip wget -q https://github.com/godotengine/godot-builds/releases/download/4.7.2-stable/Godot_v4.7.2-stable_linux.x86_64.zip # The release tag is mutable; the hash is the pin (from the release's # SHA512-SUMS.txt, verified against the downloaded archive). echo "9aa00f7a605200940bce3027a567b782f49bd8e940dd06ae9e987bd65aee1b1467edd56ed84fcdcbdd44354bf613bdbb4e5d2913e925850368e150c59ed54c65 Godot_v4.7.2-stable_linux.x86_64.zip" | sha512sum -c - unzip Godot_v4.7.2-stable_linux.x86_64.zip chmod +x Godot_v4.7.2-stable_linux.x86_64 sudo mv Godot_v4.7.2-stable_linux.x86_64 /usr/local/bin/godot - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable - name: Cache cargo uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: | ~/.cargo/registry target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Build GDExtension run: cargo build -p gpty-gdext - name: Install GDExtension library run: cp target/debug/libgpty_gdext.so godot/bin/libgpty_gdext.linux.x86_64.so - name: Import project run: godot --headless --path godot --import - name: Run GUT tests # gut-check owns the command and validates the run: GUT exits 0 after # a parse error aborts it with most of the suite unexecuted. run: ./scripts/gut-check - name: Live pane-API smoke # One harness for every platform (Unix socket here, named pipe in the # windows-smoke job); it builds the CLI and the extension itself. run: python3 ./scripts/smoke_pane_api.py # ── Windows runtime smoke (pane API over named pipes) ───────────────── # rust-windows only compiles the Windows target — nothing ever ran Godot on # Windows, so the named-pipe control and event listeners and the pane API # were inferred from source. This job boots the GUI headless on # windows-latest and drives the CLI against it: the same smoke harness as # Linux, whose only platform difference is the transport. windows-smoke: runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable - name: Cache cargo uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: | ~/.cargo/registry target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Install Godot (Windows, headless) shell: pwsh run: | Invoke-WebRequest -Uri "https://github.com/godotengine/godot-builds/releases/download/4.7.2-stable/Godot_v4.7.2-stable_win64.exe.zip" -OutFile godot.zip # The release tag is mutable; the hash is the pin (from the release's # SHA512-SUMS.txt, verified against the downloaded archive). $expected = '83decd58fdf67b9d657958a1ae6bf1929c20785315a81effe245874cdc57acb709bf868e00778a96984338c1b29dafdb453c6847747694621c6ecf5da2259993' $actual = (Get-FileHash godot.zip -Algorithm SHA512).Hash.ToLower() if ($actual -ne $expected) { throw "Godot archive checksum mismatch: $actual" } Expand-Archive godot.zip -DestinationPath godot-editor $exe = Get-ChildItem godot-editor -Filter 'Godot_v4.7.2-stable_win64.exe' | Select-Object -First 1 Rename-Item -Path $exe.FullName -NewName godot.exe Add-Content -Path $env:GITHUB_PATH -Value $exe.DirectoryName - name: Live pane-API smoke (Windows) shell: pwsh # GODOT is explicit: the step above adds the editor to PATH for later # steps, and which() is not worth relying on for a download. run: | python --version python ./scripts/smoke_pane_api.py env: GODOT: ${{ github.workspace }}\godot-editor\godot.exe - name: Import project shell: bash # `godot` is on PATH from the download step; the smoke imported the # project already, and GUT resolves `class_name` globals from that cache. run: godot --headless --path godot --import - name: GUT tests (Windows) # The same gate as Linux, under the runner's Git Bash: `gut-check` owns # the command and validates the run (a parse error aborts GUT after a # few scripts and still exits 0), so a Windows suite that stops early # cannot pass as green. GUT here runs against the platform default shell # — cmd.exe — since the suite reads `default_shell_command()`. shell: bash run: ./scripts/gut-check env: GODOT: ${{ github.workspace }}\godot-editor\godot.exe # ── Rust formatting ─────────────────────────────────────────────────── rust-fmt: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable with: components: rustfmt - name: Check formatting run: cargo fmt --all -- --check # ── Dependency audit ────────────────────────────────────────────────── cargo-audit: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable # Audit the committed Cargo.lock. Never regenerate here: # generate-lockfile re-resolves to latest-compatible versions and # silently rewrites the pinned graph, letting a fresh advisory in an # unpinned dependency fail CI independently of what we ship. - name: Verify Cargo.lock exists run: test -f Cargo.lock - name: Run cargo-audit uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2 with: token: ${{ secrets.GITHUB_TOKEN }}