Skip to content

fix(ci): use .xlings.json + dynamic package detection #86

fix(ci): use .xlings.json + dynamic package detection

fix(ci): use .xlings.json + dynamic package detection #86

Workflow file for this run

name: CI
on:
push:
branches: [main]
paths:
- 'packages/**'
- 'tests/**'
- '.github/**'
- '.xlings.json'
pull_request:
branches: [main]
paths:
- 'packages/**'
- 'tests/**'
- '.github/**'
- '.xlings.json'
env:
XLINGS_NON_INTERACTIVE: 1
jobs:
detect-changes:
runs-on: ubuntu-24.04
outputs:
packages: ${{ steps.detect.outputs.packages }}
build_all: ${{ steps.detect.outputs.build_all }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect changed packages
id: detect
run: |
# Determine the base ref for comparison
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
else
BASE="${{ github.event.before }}"
fi
# Check if CI config itself changed — if so, build all
CI_CHANGED=$(git diff --name-only "$BASE" HEAD -- .github/ .xlings.json || true)
if [ -n "$CI_CHANGED" ]; then
echo "build_all=true" >> "$GITHUB_OUTPUT"
echo "CI config changed, will build all packages"
else
echo "build_all=false" >> "$GITHUB_OUTPUT"
fi
# Collect changed package names from packages/ and tests/ directories
CHANGED_DIRS=$(git diff --name-only "$BASE" HEAD -- packages/ tests/ | \
sed -n 's|^packages/./\([^/]*\)/.*|\1|p; s|^tests/./\([^/]*\)/.*|\1|p' | \
sort -u)
# Build JSON array of changed packages
JSON="["
FIRST=true
for pkg in $CHANGED_DIRS; do
if [ "$FIRST" = true ]; then
FIRST=false
else
JSON="$JSON,"
fi
JSON="$JSON\"$pkg\""
done
JSON="$JSON]"
echo "packages=$JSON" >> "$GITHUB_OUTPUT"
echo "Changed packages: $JSON"
build:
needs: detect-changes
if: needs.detect-changes.outputs.build_all == 'true' || needs.detect-changes.outputs.packages != '[]'
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-24.04, shell: bash }
- { os: macos-15, shell: bash }
- { os: windows-latest, shell: pwsh }
runs-on: ${{ matrix.os }}
name: build (${{ matrix.os }})
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-toolchain
- name: Configure (linux)
if: runner.os == 'Linux'
working-directory: tests
run: |
GCC_VERSION=$(python3 -c "import json; d=json.load(open('../.xlings.json')); print(d['workspace']['gcc']['linux'])")
GCC_BIN="$HOME/.xlings/data/xpkgs/xim-x-gcc/${GCC_VERSION}/bin"
echo "Using gcc ${GCC_VERSION} from ${GCC_BIN}"
"${GCC_BIN}/gcc" --version | head -1
# xlings gcc needs system sysroot for headers and CRT files
export LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/usr/lib:/lib/x86_64-linux-gnu"
export C_INCLUDE_PATH="/usr/include:/usr/include/x86_64-linux-gnu"
export CPLUS_INCLUDE_PATH="/usr/include:/usr/include/x86_64-linux-gnu"
export PATH="${GCC_BIN}:$PATH"
xmake f -P . -y -vv
- name: Configure (macos)
if: runner.os == 'macOS'
working-directory: tests
run: |
LLVM_VERSION=$(python3 -c "import json; d=json.load(open('../.xlings.json')); print(d['workspace']['llvm']['macosx'])")
LLVM_BIN="$HOME/.xlings/data/xpkgs/xim-x-llvm/${LLVM_VERSION}/bin"
echo "Using llvm ${LLVM_VERSION} from ${LLVM_BIN}"
"${LLVM_BIN}/clang" --version | head -1
export PATH="${LLVM_BIN}:$PATH"
xmake f -P . -y -vv --toolchain=llvm
- name: Configure (windows)
if: runner.os == 'Windows'
working-directory: tests
run: xmake f -P . -y -vv
- name: Build and test (unix)
if: runner.os != 'Windows'
working-directory: tests
env:
BUILD_ALL: ${{ needs.detect-changes.outputs.build_all }}
CHANGED_PACKAGES: ${{ needs.detect-changes.outputs.packages }}
run: |
# Put correct compiler on PATH and set system paths for xmake subprocesses
if [ "$(uname)" = "Linux" ]; then
GCC_VERSION=$(python3 -c "import json; d=json.load(open('../.xlings.json')); print(d['workspace']['gcc']['linux'])")
export PATH="$HOME/.xlings/data/xpkgs/xim-x-gcc/${GCC_VERSION}/bin:$PATH"
export LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/usr/lib:/lib/x86_64-linux-gnu"
export C_INCLUDE_PATH="/usr/include:/usr/include/x86_64-linux-gnu"
export CPLUS_INCLUDE_PATH="/usr/include:/usr/include/x86_64-linux-gnu"
else
LLVM_VERSION=$(python3 -c "import json; d=json.load(open('../.xlings.json')); print(d['workspace']['llvm']['macosx'])")
export PATH="$HOME/.xlings/data/xpkgs/xim-x-llvm/${LLVM_VERSION}/bin:$PATH"
fi
# Collect test targets to build
TARGETS=""
if [ "$BUILD_ALL" = "true" ]; then
TARGETS=$(grep -r 'target("' */*/xmake.lua 2>/dev/null | sed 's/.*target("\([^"]*\)").*/\1/' | sort -u)
echo "Building ALL targets: $TARGETS"
else
for pkg in $(echo "$CHANGED_PACKAGES" | tr -d '[]"' | tr ',' ' '); do
TARGET="${pkg}_test"
if grep -rq "target(\"$TARGET\")" */*/xmake.lua 2>/dev/null; then
TARGETS="$TARGETS $TARGET"
else
echo "Warning: no test target '$TARGET' found for package '$pkg', skipping"
fi
done
echo "Building changed targets: $TARGETS"
fi
# Build and run each target
for target in $TARGETS; do
echo "=== Building $target ==="
xmake build -P . -y "$target"
# llmapi requires API key, only build don't run
if [ "$target" = "llmapi_test" ]; then
echo "Skipping run for $target (requires API key)"
continue
fi
echo "=== Running $target ==="
if [ "$target" = "cmdline_test" ]; then
xmake run -P . "$target" test_input
else
xmake run -P . "$target"
fi
done
- name: Build and test (windows)
if: runner.os == 'Windows'
working-directory: tests
env:
BUILD_ALL: ${{ needs.detect-changes.outputs.build_all }}
CHANGED_PACKAGES: ${{ needs.detect-changes.outputs.packages }}
run: |
# Collect test targets
$targets = @()
if ($env:BUILD_ALL -eq "true") {
$targets = Get-ChildItem -Recurse -Filter "xmake.lua" -Path "*/*" |
Select-String 'target\("([^"]+)"\)' |
ForEach-Object { $_.Matches[0].Groups[1].Value } |
Sort-Object -Unique
Write-Host "Building ALL targets: $($targets -join ', ')"
} else {
$packages = $env:CHANGED_PACKAGES | ConvertFrom-Json
foreach ($pkg in $packages) {
$target = "${pkg}_test"
$found = Get-ChildItem -Recurse -Filter "xmake.lua" -Path "*/*" |
Select-String "target\(`"$target`"\)" -Quiet
if ($found) {
$targets += $target
} else {
Write-Host "Warning: no test target '$target' found for package '$pkg', skipping"
}
}
Write-Host "Building changed targets: $($targets -join ', ')"
}
foreach ($target in $targets) {
Write-Host "=== Building $target ==="
xmake build -P . -y $target
if ($target -eq "llmapi_test") {
Write-Host "Skipping run for $target (requires API key)"
continue
}
Write-Host "=== Running $target ==="
if ($target -eq "cmdline_test") {
xmake run -P . $target test_input
} else {
xmake run -P . $target
}
}