fix(ci): use .xlings.json + dynamic package detection #92
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: [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: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| BASE="${{ github.event.pull_request.base.sha }}" | |
| else | |
| BASE="${{ github.event.before }}" | |
| fi | |
| 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 | |
| CHANGED_DIRS=$(git diff --name-only "$BASE" HEAD -- packages/ tests/ | \ | |
| sed -n 's|^packages/./\([^/]*\)/.*|\1|p; s|^tests/./\([^/]*\)/.*|\1|p' | \ | |
| sort -u) | |
| 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: xmake f -P . -y -vv | |
| - name: Configure (macos) | |
| if: runner.os == 'macOS' | |
| working-directory: tests | |
| run: 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: | | |
| 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 | |
| FAILED="" | |
| for target in $TARGETS; do | |
| echo "=== Building $target ===" | |
| if ! xmake build -P . -y "$target"; then | |
| echo "FAILED to build $target" | |
| FAILED="$FAILED $target" | |
| continue | |
| fi | |
| 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 | |
| if [ -n "$FAILED" ]; then | |
| echo "::error::Failed targets:$FAILED" | |
| exit 1 | |
| fi | |
| - 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: | | |
| $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 ', ')" | |
| } | |
| $failed = @() | |
| foreach ($target in $targets) { | |
| Write-Host "=== Building $target ===" | |
| xmake build -P . -y $target | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "FAILED to build $target" | |
| $failed += $target | |
| continue | |
| } | |
| 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 | |
| } | |
| } | |
| if ($failed.Count -gt 0) { | |
| Write-Host "::error::Failed targets: $($failed -join ', ')" | |
| exit 1 | |
| } |