c6af9e284a
Tests / catch-all (windows-latest) (push) Has been cancelled
Tests / jvm (macos-latest) (push) Has been cancelled
Tests / jvm (ubuntu-latest) (push) Has been cancelled
Tests / jvm (windows-latest) (push) Has been cancelled
Tests / native (macos-latest) (push) Has been cancelled
Tests / native (ubuntu-latest) (push) Has been cancelled
Tests / native (windows-latest) (push) Has been cancelled
Tests / niche (ubuntu-latest) (push) Has been cancelled
Tests / other-langs (macos-latest) (push) Has been cancelled
Tests / other-langs (ubuntu-latest) (push) Has been cancelled
Tests / other-langs (windows-latest) (push) Has been cancelled
Tests / catch-all (macos-latest) (push) Has been cancelled
Tests / catch-all (ubuntu-latest) (push) Has been cancelled
Docs Build / build (push) Has been cancelled
Docs Build / deploy (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Build and Push Docker Images / build-and-push (push) Has been cancelled
707 lines
35 KiB
YAML
707 lines
35 KiB
YAML
# =============================================================================
|
|
# Test CI -- strategy & rationale (details inline at each step)
|
|
# =============================================================================
|
|
# BATCHING / PARALLELISM
|
|
# Tests run as parallel matrix jobs over (os x batch). They are split into 5
|
|
# marker-based batches (jvm, native, other-langs, niche, catch-all). We batch by
|
|
# pytest MARKER, not pytest-xdist (`-n`): tests share fixtures, language-server
|
|
# processes and on-disk resources, so in-process parallelism would race them.
|
|
# Each batch installs ONLY the toolchains its languages need (every install step
|
|
# is gated on matrix.batch), keeping jobs lean. catch-all is the negation of all
|
|
# named marker groups, so every test lands in exactly one batch and the partition
|
|
# cannot desync when a language is added.
|
|
#
|
|
# OS GATING
|
|
# Default is the full (os x batch) cross-product. `niche` (heavy/slow toolchains:
|
|
# lean, ocaml, nix, julia, R, perl) is ubuntu-only via matrix.exclude -- not worth
|
|
# the Windows/macOS cost. An OS-specific *language* inside an otherwise all-OS
|
|
# batch (swift -> macOS, haskell -> Linux only) is skipped one level down by the
|
|
# central conftest guard, NOT by the matrix.
|
|
#
|
|
# CONCURRENCY LIMITS (GitHub free tier)
|
|
# Hard caps: 20 concurrent jobs total and 5 concurrent macOS jobs. These bound the
|
|
# batch count -- the current matrix is 13 jobs (4 of them macOS), inside both
|
|
# limits; adding batches/OSes must keep respecting these ceilings or jobs queue.
|
|
# `concurrency: cancel-in-progress` also kills superseded runs on the same ref to
|
|
# free capacity quickly.
|
|
#
|
|
# CACHING
|
|
# Per-batch language-server cache keyed by os+batch (language-servers-<os>-<batch>),
|
|
# plus dedicated caches for the uv venv (keyed on uv.lock), swiftly, ZLS, FPC and
|
|
# the Go build/bin. Most niche toolchains restore from cache, so that batch is fast
|
|
# despite its size.
|
|
#
|
|
# TESTING / SKIPS
|
|
# All language skip logic is centralized in test/conftest.py
|
|
# (`language_tests_enabled()`): a test is skipped when its toolchain is not expected
|
|
# on the current OS/CI. Lint and type-check are batch-independent and run once per
|
|
# OS (catch-all only), not in every batch.
|
|
# =============================================================================
|
|
name: Tests
|
|
|
|
on:
|
|
pull_request:
|
|
push:
|
|
branches:
|
|
- main
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
cpu:
|
|
name: ${{ matrix.batch }} (${{ matrix.os }})
|
|
runs-on: ${{ matrix.os }}
|
|
# Marker groups are defined once here and combined in the "Compute marker expression" step.
|
|
# The catch-all batch is derived as the negation of all named groups, so adding a language to
|
|
# any group automatically removes it from catch-all -- the partition can never desync, and no
|
|
# test can fall through to "no batch". To add/rebalance: edit a MARKERS_* var, and (for a new
|
|
# group) add a case branch below + the batch name to the matrix list.
|
|
env:
|
|
MARKERS_JVM: "java or csharp or fsharp or kotlin or scala or groovy or clojure or bsl"
|
|
MARKERS_NATIVE: "cpp or rust or go or zig or pascal or swift" # swift is macOS-only (skips elsewhere via the guard)
|
|
# other-langs: grab-bag + scripting languages + haskell. Haskell (GHC/HLS) is installed only on
|
|
# Linux -- it's too slow/expensive to build on the macOS runner -- and skips on Windows+macOS via the guard.
|
|
MARKERS_OTHER_LANGS: "ruby or php or lua or luau or bash or powershell or elixir or erlang or dart or haxe or haskell or terraform or rego or ansible or yaml or toml or markdown or latex or crystal or cue or fortran or ada or matlab or systemverilog or hlsl or msl or al"
|
|
# niche: slow, mostly-cached toolchains. ocaml + haskell are the two slowest -- kept in separate
|
|
# batches (ocaml here, haskell in other-langs). nix + perl skip on Windows via the guard.
|
|
MARKERS_NICHE: "julia or r or perl or lean4 or nix or ocaml"
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
python-version: ["3.11"]
|
|
# 5 batches, each installs ONLY the toolchains its languages need (steps gated by matrix.batch).
|
|
# Node/Python/uv stay in every batch (cheap, needed for the venv + auto-installed LSes).
|
|
# catch-all = web + python + the core framework.
|
|
batch: [jvm, native, other-langs, niche, catch-all]
|
|
# --- OS gating: which batch runs on which OS -----------------------------------------
|
|
# Default is the full cross-product (every batch on all three OSes); the `exclude` entries
|
|
# below carve out the exceptions. An OS-specific *language* inside an otherwise all-OS batch
|
|
# is handled one level down by the central conftest guard (swift -> macOS only; haskell ->
|
|
# not Windows), NOT here -- excludes drop a WHOLE batch from an OS.
|
|
# jvm, native, other-langs, catch-all -> ubuntu, windows, macOS
|
|
# niche -> ubuntu only (heavy/slow toolchains -- lean, ocaml,
|
|
# nix, julia, R, perl -- not worth the Win/macOS cost)
|
|
exclude:
|
|
- { os: windows-latest, batch: niche }
|
|
- { os: macos-latest, batch: niche }
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Set up Python ${{ matrix.python-version }}
|
|
uses: actions/setup-python@v4
|
|
with:
|
|
python-version: "${{ matrix.python-version }}"
|
|
- uses: actions/setup-go@v5
|
|
if: matrix.batch == 'native'
|
|
with:
|
|
go-version: ">=1.17.0"
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20.x'
|
|
- name: Ensure cached directory exist before calling cache-related actions
|
|
shell: bash
|
|
run: |
|
|
mkdir -p $HOME/.serena/language_servers/static
|
|
mkdir -p $HOME/.cache/go-build
|
|
mkdir -p $HOME/go/bin
|
|
- name: Install uv
|
|
# Use the official action (not `curl | sh`): it adds uv to GITHUB_PATH (the real OS PATH),
|
|
# so the Python language server's subprocess `shutil.which("uv"/"uvx")` finds it on Windows.
|
|
# The bash installer only puts uv on the Git-Bash MSYS PATH, invisible to Windows processes.
|
|
uses: astral-sh/setup-uv@v5
|
|
- name: Cache uv virtualenv
|
|
id: cache-uv
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: .venv
|
|
key: uv-venv-${{ runner.os }}-${{ matrix.python-version }}-lock-${{ hashFiles('uv.lock') }}
|
|
- name: Create virtual environment
|
|
shell: bash
|
|
run: |
|
|
if [ ! -d ".venv" ]; then
|
|
uv venv
|
|
fi
|
|
- name: Install Python environment
|
|
shell: bash
|
|
run: uv sync --extra dev --locked
|
|
- name: List Python dependencies
|
|
shell: bash
|
|
run: uv pip list
|
|
- name: Check formatting
|
|
# Lint is batch-independent; run it only once per OS instead of in every batch.
|
|
if: matrix.batch == 'catch-all'
|
|
shell: bash
|
|
run: uv run poe lint
|
|
# Add Go bin directory to PATH for this workflow
|
|
# GITHUB_PATH is a special file that GitHub Actions uses to modify PATH
|
|
# Writing to this file adds the directory to the PATH for subsequent steps
|
|
- name: Cache Go binaries
|
|
if: matrix.batch == 'native'
|
|
id: cache-go-binaries
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: |
|
|
~/go/bin
|
|
~/.cache/go-build
|
|
key: go-binaries-${{ runner.os }}-gopls-latest
|
|
- name: Install gopls
|
|
if: matrix.batch == 'native' && steps.cache-go-binaries.outputs.cache-hit != 'true'
|
|
shell: bash
|
|
run: go install golang.org/x/tools/gopls@latest
|
|
# Erlang currently not tested in CI, random hangings on macos, always hangs on ubuntu
|
|
# In local tests, erlang seems to work though
|
|
# - name: Install Erlang Language Server
|
|
# if: runner.os != 'Windows'
|
|
# shell: bash
|
|
# run: |
|
|
# # Install rebar3 if not already available
|
|
# which rebar3 || (curl -fsSL https://github.com/erlang/rebar3/releases/download/3.23.0/rebar3 -o /tmp/rebar3 && chmod +x /tmp/rebar3 && sudo mv /tmp/rebar3 /usr/local/bin/rebar3)
|
|
# # Clone and build erlang_ls
|
|
# git clone https://github.com/erlang-ls/erlang_ls.git /tmp/erlang_ls
|
|
# cd /tmp/erlang_ls
|
|
# make install PREFIX=/usr/local
|
|
# # Ensure erlang_ls is in PATH
|
|
# echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
- name: Install clojure tools
|
|
if: matrix.batch == 'jvm'
|
|
uses: DeLaGuardo/setup-clojure@13.4
|
|
with:
|
|
cli: latest
|
|
- name: Install ccls (C/C++ Language Server)
|
|
if: matrix.batch == 'native'
|
|
shell: bash
|
|
run: |
|
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y ccls
|
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
|
brew install ccls
|
|
elif [[ "${{ runner.os }}" == "Windows" ]]; then
|
|
choco install ccls -y
|
|
fi
|
|
# Verify installation
|
|
if command -v ccls &> /dev/null; then
|
|
echo "ccls installed: $(ccls --version 2>&1 | head -1)"
|
|
else
|
|
echo "ERROR: ccls installation failed"
|
|
exit 1
|
|
fi
|
|
- name: Setup Java (for JVM based languages)
|
|
if: matrix.batch == 'jvm'
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: 'temurin'
|
|
java-version: '21'
|
|
- name: Setup .NET SDK (for F# and C# languages)
|
|
if: matrix.batch == 'jvm'
|
|
uses: actions/setup-dotnet@v4
|
|
with:
|
|
dotnet-version: '10.0.x'
|
|
- name: List .NET runtimes
|
|
if: matrix.batch == 'jvm'
|
|
shell: bash
|
|
run: dotnet --list-runtimes
|
|
- name: Install Terraform
|
|
if: matrix.batch == 'other-langs'
|
|
uses: hashicorp/setup-terraform@v3
|
|
with:
|
|
terraform_version: "1.5.0"
|
|
terraform_wrapper: false
|
|
# - name: Install swift
|
|
# if: runner.os != 'Windows'
|
|
# uses: swift-actions/setup-swift@v2
|
|
# Installation of swift with the action screws with installation of ruby on macOS for some reason
|
|
# We can try again when version 3 of the action is released, where they will also use swiftly
|
|
# Until then, we use custom code to install swift. Sourcekit-lsp is installed automatically with swift
|
|
- name: Cache Swift toolchain (swiftly)
|
|
if: runner.os == 'macOS' && matrix.batch == 'native'
|
|
id: cache-swiftly
|
|
uses: actions/cache@v3
|
|
with:
|
|
# macOS keeps the toolchain (incl. sourcekit-lsp) under ~/Library/Developer/Toolchains,
|
|
# NOT under ~/.swiftly -- both must be cached or the toolchain is lost on restore.
|
|
path: |
|
|
~/.swiftly
|
|
~/Library/Developer/Toolchains
|
|
key: swiftly-${{ runner.os }}-6.1.2-v2
|
|
- name: Install Swift with swiftly (macOS)
|
|
if: runner.os == 'macOS' && matrix.batch == 'native'
|
|
run: |
|
|
set -e
|
|
# Install swiftly itself only if its binary isn't already present (e.g. restored from cache).
|
|
if [ ! -x "$HOME/.swiftly/bin/swiftly" ]; then
|
|
echo "=== Installing swiftly ==="
|
|
curl -O https://download.swift.org/swiftly/darwin/swiftly.pkg
|
|
installer -pkg swiftly.pkg -target CurrentUserHomeDirectory
|
|
"$HOME/.swiftly/bin/swiftly" init --quiet-shell-followup
|
|
fi
|
|
. "${SWIFTLY_HOME_DIR:-$HOME/.swiftly}/env.sh"
|
|
hash -r
|
|
# Always run install: it downloads on a cache miss and is a fast no-op when the toolchain is
|
|
# genuinely present from cache. No `--use` -- that triggers the interactive .swift-version prompt.
|
|
swiftly install 6.1.2
|
|
# Locate sourcekit-lsp directly (macOS toolchains live under ~/Library/Developer/Toolchains)
|
|
# and put its directory on PATH -- avoids `swiftly use`, which prompts/hangs in CI.
|
|
SK="$(find "$HOME/Library/Developer/Toolchains" "$HOME/.swiftly" -name sourcekit-lsp 2>/dev/null | head -1)"
|
|
if [ -z "$SK" ]; then
|
|
echo "ERROR: sourcekit-lsp not found after Swift install. Searched:"
|
|
ls -la "$HOME/Library/Developer/Toolchains" 2>/dev/null || echo " (no ~/Library/Developer/Toolchains)"
|
|
ls -la "$HOME/.swiftly" 2>/dev/null || echo " (no ~/.swiftly)"
|
|
exit 1
|
|
fi
|
|
echo "sourcekit-lsp: $SK"
|
|
dirname "$SK" >> "$GITHUB_PATH"
|
|
- name: Install Ruby
|
|
if: matrix.batch == 'other-langs'
|
|
uses: ruby/setup-ruby@v1
|
|
with:
|
|
ruby-version: '3.4'
|
|
- name: Install Ruby language server
|
|
if: matrix.batch == 'other-langs'
|
|
shell: bash
|
|
run: gem install ruby-lsp
|
|
- name: Install Zig
|
|
if: matrix.batch == 'native'
|
|
uses: mlugg/setup-zig@v2
|
|
with:
|
|
version: 0.14.1
|
|
- name: Cache ZLS (Zig Language Server)
|
|
if: matrix.batch == 'native'
|
|
id: cache-zls
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: ~/.zls
|
|
key: zls-${{ runner.os }}-0.14.0
|
|
- name: Install ZLS (Zig Language Server)
|
|
if: matrix.batch == 'native'
|
|
shell: bash
|
|
run: |
|
|
ZLS_DIR="$HOME/.zls"
|
|
if [[ "${{ steps.cache-zls.outputs.cache-hit }}" != "true" ]]; then
|
|
mkdir -p "$ZLS_DIR"
|
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
|
wget -O /tmp/zls.tar.xz https://github.com/zigtools/zls/releases/download/0.14.0/zls-x86_64-linux.tar.xz
|
|
tar -xf /tmp/zls.tar.xz -C "$ZLS_DIR" && rm /tmp/zls.tar.xz
|
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
|
wget -O /tmp/zls.tar.xz https://github.com/zigtools/zls/releases/download/0.14.0/zls-x86_64-macos.tar.xz
|
|
tar -xf /tmp/zls.tar.xz -C "$ZLS_DIR" && rm /tmp/zls.tar.xz
|
|
elif [[ "${{ runner.os }}" == "Windows" ]]; then
|
|
curl -L -o zls.zip https://github.com/zigtools/zls/releases/download/0.14.0/zls-x86_64-windows.zip
|
|
unzip -o zls.zip -d "$ZLS_DIR" && rm zls.zip
|
|
fi
|
|
chmod +x "$ZLS_DIR/zls" 2>/dev/null || true
|
|
fi
|
|
echo "$ZLS_DIR" >> "$GITHUB_PATH"
|
|
- name: Install verible-verilog-ls (SystemVerilog Language Server)
|
|
if: matrix.batch == 'other-langs'
|
|
shell: bash
|
|
run: |
|
|
VERIBLE_VERSION="v0.0-4051-g9fdb4057"
|
|
|
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
|
wget https://github.com/chipsalliance/verible/releases/download/${VERIBLE_VERSION}/verible-${VERIBLE_VERSION}-linux-static-x86_64.tar.gz
|
|
tar -xzf verible-${VERIBLE_VERSION}-linux-static-x86_64.tar.gz
|
|
sudo mv verible-${VERIBLE_VERSION}/bin/verible-verilog-ls /usr/local/bin/
|
|
rm -rf verible-${VERIBLE_VERSION} verible-${VERIBLE_VERSION}-linux-static-x86_64.tar.gz
|
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
|
wget https://github.com/chipsalliance/verible/releases/download/${VERIBLE_VERSION}/verible-${VERIBLE_VERSION}-macOS.tar.gz
|
|
tar -xzf verible-${VERIBLE_VERSION}-macOS.tar.gz
|
|
sudo mv verible-${VERIBLE_VERSION}-macOS/bin/verible-verilog-ls /usr/local/bin/
|
|
rm -rf verible-${VERIBLE_VERSION}-macOS verible-${VERIBLE_VERSION}-macOS.tar.gz
|
|
elif [[ "${{ runner.os }}" == "Windows" ]]; then
|
|
curl -L -o verible.zip https://github.com/chipsalliance/verible/releases/download/${VERIBLE_VERSION}/verible-${VERIBLE_VERSION}-win64.zip
|
|
unzip -o verible.zip
|
|
mkdir -p "$HOME/bin"
|
|
mv verible-${VERIBLE_VERSION}-win64/verible-verilog-ls.exe "$HOME/bin/"
|
|
echo "$HOME/bin" >> $GITHUB_PATH
|
|
rm -rf verible-${VERIBLE_VERSION}-win64 verible.zip
|
|
fi
|
|
|
|
# Verify installation
|
|
if command -v verible-verilog-ls &> /dev/null; then
|
|
echo "verible-verilog-ls installed successfully"
|
|
else
|
|
echo "WARNING: verible-verilog-ls not found in PATH"
|
|
fi
|
|
- name: Install Lua Language Server
|
|
if: matrix.batch == 'other-langs'
|
|
shell: bash
|
|
run: |
|
|
LUA_LS_VERSION="3.15.0"
|
|
LUA_LS_DIR="$HOME/.serena/language_servers/lua"
|
|
mkdir -p "$LUA_LS_DIR"
|
|
|
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
|
if [[ "$(uname -m)" == "x86_64" ]]; then
|
|
wget https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-linux-x64.tar.gz
|
|
tar -xzf lua-language-server-${LUA_LS_VERSION}-linux-x64.tar.gz -C "$LUA_LS_DIR"
|
|
else
|
|
wget https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-linux-arm64.tar.gz
|
|
tar -xzf lua-language-server-${LUA_LS_VERSION}-linux-arm64.tar.gz -C "$LUA_LS_DIR"
|
|
fi
|
|
chmod +x "$LUA_LS_DIR/bin/lua-language-server"
|
|
# Create wrapper script instead of symlink to ensure supporting files are found
|
|
echo '#!/bin/bash' | sudo tee /usr/local/bin/lua-language-server > /dev/null
|
|
echo 'cd "${HOME}/.serena/language_servers/lua/bin"' | sudo tee -a /usr/local/bin/lua-language-server > /dev/null
|
|
echo 'exec ./lua-language-server "$@"' | sudo tee -a /usr/local/bin/lua-language-server > /dev/null
|
|
sudo chmod +x /usr/local/bin/lua-language-server
|
|
rm lua-language-server-*.tar.gz
|
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
|
if [[ "$(uname -m)" == "x86_64" ]]; then
|
|
wget https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-darwin-x64.tar.gz
|
|
tar -xzf lua-language-server-${LUA_LS_VERSION}-darwin-x64.tar.gz -C "$LUA_LS_DIR"
|
|
else
|
|
wget https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-darwin-arm64.tar.gz
|
|
tar -xzf lua-language-server-${LUA_LS_VERSION}-darwin-arm64.tar.gz -C "$LUA_LS_DIR"
|
|
fi
|
|
chmod +x "$LUA_LS_DIR/bin/lua-language-server"
|
|
# Create wrapper script instead of symlink to ensure supporting files are found
|
|
echo '#!/bin/bash' | sudo tee /usr/local/bin/lua-language-server > /dev/null
|
|
echo 'cd "${HOME}/.serena/language_servers/lua/bin"' | sudo tee -a /usr/local/bin/lua-language-server > /dev/null
|
|
echo 'exec ./lua-language-server "$@"' | sudo tee -a /usr/local/bin/lua-language-server > /dev/null
|
|
sudo chmod +x /usr/local/bin/lua-language-server
|
|
rm lua-language-server-*.tar.gz
|
|
elif [[ "${{ runner.os }}" == "Windows" ]]; then
|
|
curl -L -o lua-ls.zip https://github.com/LuaLS/lua-language-server/releases/download/${LUA_LS_VERSION}/lua-language-server-${LUA_LS_VERSION}-win32-x64.zip
|
|
unzip -o lua-ls.zip -d "$LUA_LS_DIR"
|
|
# For Windows, we'll add the bin directory directly to PATH
|
|
# The lua-language-server.exe can find its supporting files relative to its location
|
|
echo "$LUA_LS_DIR/bin" >> $GITHUB_PATH
|
|
rm lua-ls.zip
|
|
fi
|
|
- name: Install ansible-core and ansible-lint (for Ansible language server tests)
|
|
if: matrix.batch == 'other-langs'
|
|
shell: bash
|
|
run: uv run pip install ansible-core ansible-lint
|
|
- name: Install Haxe
|
|
if: matrix.batch == 'other-langs'
|
|
# The Haxe LS binary (server.js) is auto-downloaded and runs on Node.js,
|
|
# but it delegates to the Haxe compiler for code analysis at runtime.
|
|
uses: krdlab/setup-haxe@v2
|
|
with:
|
|
haxe-version: 4.3.7
|
|
- name: Install Elm
|
|
if: matrix.batch == 'catch-all'
|
|
shell: bash
|
|
run: npm install -g elm@0.19.1-6
|
|
- name: Install Regal (Rego Language Server)
|
|
if: matrix.batch == 'other-langs'
|
|
shell: bash
|
|
run: |
|
|
REGAL_VERSION="0.39.0"
|
|
|
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
|
if [[ "$(uname -m)" == "x86_64" ]]; then
|
|
curl -L -o regal https://github.com/StyraInc/regal/releases/download/v${REGAL_VERSION}/regal_Linux_x86_64
|
|
else
|
|
curl -L -o regal https://github.com/StyraInc/regal/releases/download/v${REGAL_VERSION}/regal_Linux_arm64
|
|
fi
|
|
chmod +x regal
|
|
sudo mv regal /usr/local/bin/
|
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
|
if [[ "$(uname -m)" == "x86_64" ]]; then
|
|
curl -L -o regal https://github.com/StyraInc/regal/releases/download/v${REGAL_VERSION}/regal_Darwin_x86_64
|
|
else
|
|
curl -L -o regal https://github.com/StyraInc/regal/releases/download/v${REGAL_VERSION}/regal_Darwin_arm64
|
|
fi
|
|
chmod +x regal
|
|
sudo mv regal /usr/local/bin/
|
|
elif [[ "${{ runner.os }}" == "Windows" ]]; then
|
|
curl -L -o regal.exe https://github.com/StyraInc/regal/releases/download/v${REGAL_VERSION}/regal_Windows_x86_64.exe
|
|
mkdir -p "$HOME/bin"
|
|
mv regal.exe "$HOME/bin/"
|
|
echo "$HOME/bin" >> $GITHUB_PATH
|
|
fi
|
|
- name: Cache Free Pascal Compiler
|
|
if: matrix.batch == 'native'
|
|
id: cache-fpc
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: |
|
|
~/fpc
|
|
~/fpcsrc
|
|
key: fpc-${{ runner.os }}-3.2.2
|
|
- name: Install Free Pascal Compiler
|
|
if: matrix.batch == 'native'
|
|
shell: bash
|
|
run: |
|
|
# Only the downloads are gated on the cache; the PP/FPCDIR/PATH detection always runs
|
|
# (it works the same on freshly-downloaded or cache-restored ~/fpc + ~/fpcsrc).
|
|
FPC_CACHE_HIT="${{ steps.cache-fpc.outputs.cache-hit }}"
|
|
FPC_VERSION="3.2.2"
|
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y fpc fpc-source
|
|
echo "PP=/usr/bin/fpc" >> $GITHUB_ENV
|
|
FPCDIR=$(ls -d /usr/share/fpcsrc/*/ 2>/dev/null | head -1 | sed 's:/$::')
|
|
[[ -z "$FPCDIR" ]] && FPCDIR="/usr/share/fpcsrc"
|
|
echo "FPCDIR=$FPCDIR" >> $GITHUB_ENV
|
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
|
command -v fpc >/dev/null || brew install fpc
|
|
if [[ "$FPC_CACHE_HIT" != "true" ]]; then
|
|
# FPC source from SourceForge (fpc-src-laz cask is incompatible with ARM64)
|
|
curl -L -o fpc-source.tar.gz "https://sourceforge.net/projects/freepascal/files/Source/${FPC_VERSION}/fpc-${FPC_VERSION}.source.tar.gz/download"
|
|
mkdir -p "$HOME/fpcsrc"
|
|
tar -xzf fpc-source.tar.gz -C "$HOME/fpcsrc"
|
|
rm fpc-source.tar.gz
|
|
fi
|
|
if [[ -d "$HOME/fpcsrc/fpc-${FPC_VERSION}/rtl" ]]; then
|
|
FPCDIR="$HOME/fpcsrc/fpc-${FPC_VERSION}"
|
|
elif [[ -d "$HOME/fpcsrc/fpc-${FPC_VERSION}/fpc-${FPC_VERSION}/rtl" ]]; then
|
|
FPCDIR="$HOME/fpcsrc/fpc-${FPC_VERSION}/fpc-${FPC_VERSION}"
|
|
else
|
|
FPCDIR="$HOME/fpcsrc/fpc-${FPC_VERSION}"
|
|
fi
|
|
echo "PP=$(which fpc)" >> $GITHUB_ENV
|
|
echo "FPCDIR=$FPCDIR" >> $GITHUB_ENV
|
|
elif [[ "${{ runner.os }}" == "Windows" ]]; then
|
|
if [[ "$FPC_CACHE_HIT" != "true" ]]; then
|
|
# freepascal-ootb (compiler) + source (ootb ships compiled units only, not source)
|
|
curl -L -o fpc-ootb.zip https://github.com/fredvs/freepascal-ootb/releases/download/${FPC_VERSION}/fpc-ootb-322-x86_64-win64.zip
|
|
mkdir -p "$HOME/fpc"
|
|
unzip -q fpc-ootb.zip -d "$HOME/fpc"
|
|
rm fpc-ootb.zip
|
|
curl -L -o fpc-source.zip "https://sourceforge.net/projects/freepascal/files/Source/${FPC_VERSION}/fpc-${FPC_VERSION}.source.zip/download"
|
|
mkdir -p "$HOME/fpcsrc"
|
|
unzip -q fpc-source.zip -d "$HOME/fpcsrc"
|
|
rm fpc-source.zip
|
|
fi
|
|
FPC_EXE=$(find "$HOME/fpc" -name "fpc-ootb-64.exe" -type f 2>/dev/null | head -1)
|
|
echo "Found FPC executable: $FPC_EXE"
|
|
echo "PP=$FPC_EXE" >> $GITHUB_ENV
|
|
echo "FPCDIR=$HOME/fpcsrc/fpc-${FPC_VERSION}" >> $GITHUB_ENV
|
|
echo "$(dirname "$FPC_EXE")" >> $GITHUB_PATH
|
|
fi
|
|
- name: Verify FPC installation
|
|
if: matrix.batch == 'native'
|
|
shell: bash
|
|
run: |
|
|
echo "=== Environment variables ==="
|
|
echo "PP=$PP"
|
|
echo "FPCDIR=$FPCDIR"
|
|
|
|
# Create a simple test program
|
|
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
|
TEST_PAS="$TEMP/fpc_test.pas"
|
|
TEST_OUT="$TEMP/fpc_test"
|
|
else
|
|
TEST_PAS="/tmp/fpc_test.pas"
|
|
TEST_OUT="/tmp/fpc_test"
|
|
fi
|
|
echo "program fpc_test; begin writeln('FPC works'); end." > "$TEST_PAS"
|
|
|
|
# Compile using PP (the compiler we actually use in tests)
|
|
echo "=== Compiling test program with PP=$PP ==="
|
|
if [[ -n "$PP" ]]; then
|
|
"$PP" "$TEST_PAS" -o"$TEST_OUT" 2>&1
|
|
else
|
|
echo "ERROR: PP environment variable is not set"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify output binary exists
|
|
if [[ -f "$TEST_OUT" ]] || [[ -f "${TEST_OUT}.exe" ]]; then
|
|
echo "FPC compilation test PASSED"
|
|
else
|
|
echo "ERROR: FPC compilation failed - no output binary at $TEST_OUT"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify FPCDIR exists (required for pasls)
|
|
if [[ -d "$FPCDIR" ]]; then
|
|
echo "FPCDIR exists: $FPCDIR"
|
|
else
|
|
echo "ERROR: FPCDIR does not exist: $FPCDIR"
|
|
exit 1
|
|
fi
|
|
# --- Slow / heavy language toolchains, each isolated in its own batch (OS gating via matrix excludes) ---
|
|
- name: Setup Haskell toolchain
|
|
if: matrix.batch == 'other-langs' && runner.os == 'Linux'
|
|
uses: haskell/ghcup-setup@v1
|
|
with:
|
|
ghc: '9.12.2'
|
|
cabal: '3.10.3.0'
|
|
hls: '2.11.0.0'
|
|
- name: Verify Haskell tools
|
|
if: matrix.batch == 'other-langs' && runner.os == 'Linux'
|
|
shell: bash
|
|
run: |
|
|
which ghc && ghc --version
|
|
which cabal && cabal --version
|
|
if command -v haskell-language-server-wrapper &>/dev/null; then
|
|
haskell-language-server-wrapper --version || echo "WARNING: HLS wrapper version check failed"
|
|
else
|
|
echo "WARNING: haskell-language-server-wrapper not found (may be incompatible with GHC 9.12.2)"
|
|
fi
|
|
- name: Pre-build Haskell test project for HLS
|
|
if: matrix.batch == 'other-langs' && runner.os == 'Linux'
|
|
shell: bash
|
|
run: |
|
|
cd test/resources/repos/haskell/test_repo
|
|
cabal update
|
|
cabal build --only-dependencies
|
|
cabal build
|
|
- name: Install OCaml and opam
|
|
if: matrix.batch == 'niche'
|
|
uses: ocaml/setup-ocaml@v3
|
|
with:
|
|
ocaml-compiler: ${{ runner.os == 'Windows' && '4.14' || '5.3.x' }}
|
|
dune-cache: true
|
|
opam-repositories: |
|
|
${{ runner.os == 'Windows' && 'opam-repository-mingw: https://github.com/ocaml-opam/opam-repository-mingw.git#sunset' || '' }}
|
|
default: https://github.com/ocaml/opam-repository.git
|
|
- name: Install OCaml packages
|
|
if: matrix.batch == 'niche'
|
|
shell: bash
|
|
run: |
|
|
if [ "$RUNNER_OS" = "Windows" ]; then
|
|
opam install -y dune ocaml-lsp-server
|
|
else
|
|
opam install -y dune 'ocaml-lsp-server>=1.23.0'
|
|
fi
|
|
- name: Install Nix
|
|
if: matrix.batch == 'niche' && runner.os != 'Windows'
|
|
uses: cachix/install-nix-action@v30
|
|
with:
|
|
nix_path: nixpkgs=channel:nixos-unstable
|
|
# Authenticate any GitHub fetch Nix makes during evaluation/tests with the runner's
|
|
# GITHUB_TOKEN (raises the unauthenticated 60 req/hr-per-IP API limit to ~1000/hr).
|
|
# nixd itself is installed from the channel below, so this is just insurance.
|
|
extra_nix_config: |
|
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
|
- name: Install nixd (Nix Language Server)
|
|
if: matrix.batch == 'niche' && runner.os != 'Windows'
|
|
shell: bash
|
|
run: |
|
|
# Install nixd from the nixos-unstable channel (set as <nixpkgs> on NIX_PATH via nix_path
|
|
# above) using classic nix-env. `-f '<nixpkgs>'` resolves the channel tarball from
|
|
# nixos.org and selects the `nixd` attribute -- it never touches the GitHub API. (The flake
|
|
# form `nix profile install github:nix-community/nixd` resolves an unpinned HEAD through
|
|
# api.github.com/.../commits/HEAD, which GitHub 429-throttles on CI even with a token;
|
|
# and `nix-env -iA nixpkgs.nixd` fails here because no channel named `nixpkgs` is registered.)
|
|
nix-env -f '<nixpkgs>' -iA nixd
|
|
if ! command -v nixd &> /dev/null; then
|
|
echo "nixd installation failed or not in PATH"; exit 1
|
|
fi
|
|
echo "$HOME/.nix-profile/bin" >> $GITHUB_PATH
|
|
- name: Build Lean 4 test project
|
|
if: matrix.batch == 'niche'
|
|
uses: leanprover/lean-action@v1
|
|
with:
|
|
lake-package-directory: test/resources/repos/lean4/test_repo
|
|
# --- niche batch: R, Julia, Perl ---
|
|
- name: Install R
|
|
if: matrix.batch == 'niche'
|
|
uses: r-lib/actions/setup-r@v2
|
|
with:
|
|
r-version: '4.4.2'
|
|
use-public-rspm: true
|
|
- name: Cache R packages (languageserver + its deps)
|
|
if: matrix.batch == 'niche'
|
|
id: cache-r
|
|
uses: actions/cache@v3
|
|
with:
|
|
# setup-r exports R_LIBS_USER as $RUNNER_TEMP/Library (a stable path) and re-exports it on
|
|
# every run -- including cache hits -- so R finds the restored packages with no extra env.
|
|
# Bump the trailing -vN (or r-version) to invalidate after an R/OS-image upgrade.
|
|
path: ${{ env.R_LIBS_USER }}
|
|
key: r-pkgs-${{ runner.os }}-R4.4.2-languageserver-v1
|
|
- name: Install R sysdeps (libuv for fs package)
|
|
# Always run (NOT gated on the cache): the cached, compiled `fs` package links libuv at
|
|
# runtime, so the shared lib must be present even when the build below is skipped.
|
|
if: matrix.batch == 'niche' && runner.os == 'Linux'
|
|
shell: bash
|
|
run: sudo apt-get update && sudo apt-get install -y libuv1-dev
|
|
- name: Install R language server
|
|
if: matrix.batch == 'niche' && steps.cache-r.outputs.cache-hit != 'true'
|
|
shell: bash
|
|
run: Rscript -e "install.packages('languageserver', repos='https://cloud.r-project.org')"
|
|
- name: Set up Julia
|
|
if: matrix.batch == 'niche'
|
|
uses: julia-actions/setup-julia@v2
|
|
with:
|
|
version: '1.12.6'
|
|
- name: Cache Julia depot (packages + precompiled)
|
|
# Caches the depot (~/.julia: packages, artifacts, compiled, ...) with an auto-managed key,
|
|
# so the LanguageServer Pkg.add below is a near-no-op (already installed + precompiled) on
|
|
# warm runs. The action handles save/restore itself -- no cache-hit gating needed.
|
|
if: matrix.batch == 'niche'
|
|
uses: julia-actions/cache@v2
|
|
- name: Install Julia LanguageServer
|
|
if: matrix.batch == 'niche'
|
|
shell: bash
|
|
run: julia -e 'using Pkg; Pkg.add(Pkg.PackageSpec(name="LanguageServer", version="5.0.0"))'
|
|
- name: Cache Perl::LanguageServer (~/perl5)
|
|
if: matrix.batch == 'niche' && runner.os != 'Windows'
|
|
id: cache-perl
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: ~/perl5
|
|
key: perl-ls-${{ runner.os }}-v1
|
|
- name: Install Perl::LanguageServer
|
|
if: matrix.batch == 'niche' && runner.os != 'Windows'
|
|
shell: bash
|
|
run: |
|
|
# System deps are cheap and needed at runtime (the cached LS links libanyevent/libio-aio),
|
|
# so install them always; the slow cpanm build (XS compile of Coro etc.) runs only on a miss.
|
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y cpanminus build-essential libanyevent-perl libio-aio-perl
|
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
|
brew install cpanminus
|
|
fi
|
|
if [[ "${{ steps.cache-perl.outputs.cache-hit }}" != "true" ]]; then
|
|
# --local-lib pins the install to ~/perl5 (matching the PERL5LIB exports below), so the
|
|
# cached directory is exactly what `perl -MPerl::LanguageServer` loads on a restore.
|
|
PERL_MM_USE_DEFAULT=1 cpanm --notest --force --local-lib="$HOME/perl5" Perl::LanguageServer
|
|
fi
|
|
echo "PERL5LIB=$HOME/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}" >> $GITHUB_ENV
|
|
echo "PERL_LOCAL_LIB_ROOT=$HOME/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}" >> $GITHUB_ENV
|
|
echo "PERL_MB_OPT=--install_base \"$HOME/perl5\"" >> $GITHUB_ENV
|
|
echo "PERL_MM_OPT=INSTALL_BASE=$HOME/perl5" >> $GITHUB_ENV
|
|
echo "$HOME/perl5/bin" >> $GITHUB_PATH
|
|
- name: Install npm deps for Angular test repo
|
|
if: matrix.batch == 'catch-all'
|
|
shell: bash
|
|
run: |
|
|
if [ -d test/resources/repos/angular/test_repo ]; then
|
|
cd test/resources/repos/angular/test_repo
|
|
npm install --no-audit --no-fund --loglevel=warn
|
|
fi
|
|
- name: Cache language servers
|
|
id: cache-language-servers
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: ~/.serena/language_servers/static
|
|
key: language-servers-${{ runner.os }}-${{ matrix.batch }}-v1
|
|
restore-keys: |
|
|
language-servers-${{ runner.os }}-${{ matrix.batch }}-
|
|
- name: Compute marker expression for this batch
|
|
id: markers
|
|
shell: bash
|
|
run: |
|
|
case "${{ matrix.batch }}" in
|
|
jvm) expr="$MARKERS_JVM" ;;
|
|
native) expr="$MARKERS_NATIVE" ;;
|
|
other-langs) expr="$MARKERS_OTHER_LANGS" ;;
|
|
niche) expr="$MARKERS_NICHE" ;;
|
|
catch-all) expr="not ($MARKERS_JVM or $MARKERS_NATIVE or $MARKERS_OTHER_LANGS or $MARKERS_NICHE)" ;;
|
|
*) echo "Unknown batch: ${{ matrix.batch }}" >&2; exit 1 ;;
|
|
esac
|
|
echo "Marker expression: $expr"
|
|
echo "expr=$expr" >> "$GITHUB_OUTPUT"
|
|
- name: Test with pytest
|
|
shell: bash
|
|
run: uv run poe test -m "${{ steps.markers.outputs.expr }}" -q --tb=short
|
|
- name: Type-checking with ty
|
|
# Type-checking is batch-independent; run it only once per OS instead of in every batch.
|
|
if: matrix.batch == 'catch-all'
|
|
shell: bash
|
|
run: uv run poe type-check
|