66 paths :
77 - ' packages/**'
88 - ' tests/**'
9- - ' .github/workflows/ci.yml'
9+ - ' .github/**'
10+ - ' .xlings.json'
1011 pull_request :
1112 branches : [main]
1213 paths :
1314 - ' packages/**'
1415 - ' tests/**'
15- - ' .github/workflows/ci.yml'
16+ - ' .github/**'
17+ - ' .xlings.json'
1618
1719env :
1820 XLINGS_NON_INTERACTIVE : 1
@@ -21,46 +23,48 @@ jobs:
2123 detect-changes :
2224 runs-on : ubuntu-24.04
2325 outputs :
24- templates : ${{ steps.filter.outputs.templates }}
25- cmdline : ${{ steps.filter.outputs.cmdline }}
26- llmapi : ${{ steps.filter.outputs.llmapi }}
27- lua : ${{ steps.filter.outputs.lua }}
28- xpkg : ${{ steps.filter.outputs.xpkg }}
26+ packages : ${{ steps.detect.outputs.packages }}
27+ build_all : ${{ steps.detect.outputs.build_all }}
2928 steps :
3029 - uses : actions/checkout@v4
31- - uses : dorny/paths-filter@v3
32- id : filter
3330 with :
34- filters : |
35- templates:
36- - 'packages/t/templates/**'
37- - 'tests/t/templates/**'
38- - '.github/workflows/ci.yml'
39- cmdline:
40- - 'packages/c/cmdline/**'
41- - 'tests/c/cmdline/**'
42- - '.github/workflows/ci.yml'
43- llmapi:
44- - 'packages/l/llmapi/**'
45- - 'tests/l/llmapi/**'
46- - '.github/workflows/ci.yml'
47- lua:
48- - 'packages/m/mcpplibs-capi-lua/**'
49- - 'tests/l/lua/**'
50- - '.github/workflows/ci.yml'
51- xpkg:
52- - 'packages/m/mcpplibs-xpkg/**'
53- - 'tests/m/mcpplibs-xpkg/**'
54- - '.github/workflows/ci.yml'
31+ fetch-depth : 0
32+
33+ - name : Detect changed packages
34+ id : detect
35+ run : |
36+ if [ "${{ github.event_name }}" = "pull_request" ]; then
37+ BASE="${{ github.event.pull_request.base.sha }}"
38+ else
39+ BASE="${{ github.event.before }}"
40+ fi
41+
42+ CI_CHANGED=$(git diff --name-only "$BASE" HEAD -- .github/ .xlings.json || true)
43+ if [ -n "$CI_CHANGED" ]; then
44+ echo "build_all=true" >> "$GITHUB_OUTPUT"
45+ echo "CI config changed, will build all packages"
46+ else
47+ echo "build_all=false" >> "$GITHUB_OUTPUT"
48+ fi
49+
50+ CHANGED_DIRS=$(git diff --name-only "$BASE" HEAD -- packages/ tests/ | \
51+ sed -n 's|^packages/./\([^/]*\)/.*|\1|p; s|^tests/./\([^/]*\)/.*|\1|p' | \
52+ sort -u)
53+
54+ JSON="["
55+ FIRST=true
56+ for pkg in $CHANGED_DIRS; do
57+ if [ "$FIRST" = true ]; then FIRST=false; else JSON="$JSON,"; fi
58+ JSON="$JSON\"$pkg\""
59+ done
60+ JSON="$JSON]"
61+
62+ echo "packages=$JSON" >> "$GITHUB_OUTPUT"
63+ echo "Changed packages: $JSON"
5564
5665 build :
5766 needs : detect-changes
58- if : >-
59- needs.detect-changes.outputs.templates == 'true' ||
60- needs.detect-changes.outputs.cmdline == 'true' ||
61- needs.detect-changes.outputs.llmapi == 'true' ||
62- needs.detect-changes.outputs.lua == 'true' ||
63- needs.detect-changes.outputs.xpkg == 'true'
67+ if : needs.detect-changes.outputs.build_all == 'true' || needs.detect-changes.outputs.packages != '[]'
6468 strategy :
6569 fail-fast : false
6670 matrix :
@@ -78,97 +82,128 @@ jobs:
7882 if : runner.os == 'Linux'
7983 working-directory : tests
8084 run : |
81- export PATH="$HOME/.xlings/subos/current/bin:$PATH"
82- xmake f -P . -y
85+ MUSL_VER=$(python3 -c "import json; print(json.load(open('../.xlings.json'))['workspace']['musl-gcc']['linux'])")
86+ MUSL_SDK="$HOME/.xlings/data/xpkgs/xim-x-musl-gcc/${MUSL_VER}"
87+ xmake f -P . -y -vv \
88+ --sdk="${MUSL_SDK}" \
89+ --cross=x86_64-linux-musl- \
90+ --cc="${MUSL_SDK}/bin/x86_64-linux-musl-gcc" \
91+ --cxx="${MUSL_SDK}/bin/x86_64-linux-musl-g++"
8392
8493 - name : Configure (macos)
8594 if : runner.os == 'macOS'
8695 working-directory : tests
8796 run : |
88- export PATH="$HOME/.xlings/subos/current/bin:$PATH"
89- xmake f -P . -y --toolchain=llvm
97+ LLVM_VER=$(python3 -c "import json; print(json.load(open('../.xlings.json'))['workspace']['llvm']['macosx'])")
98+ LLVM_SDK="$HOME/.xlings/data/xpkgs/xim-x-llvm/${LLVM_VER}"
99+ xmake f -P . -y -vv --toolchain=llvm --sdk="${LLVM_SDK}"
90100
91101 - name : Configure (windows)
92102 if : runner.os == 'Windows'
93103 working-directory : tests
94- run : |
95- $env:PATH = "$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
96- xmake f -P . -y
104+ run : xmake f -P . -y -vv
97105
98- # templates
99- - name : templates (unix)
100- if : runner.os != 'Windows' && needs.detect-changes.outputs.templates == 'true'
106+ - name : Build and test (unix)
107+ if : runner.os != 'Windows'
101108 working-directory : tests
109+ env :
110+ BUILD_ALL : ${{ needs.detect-changes.outputs.build_all }}
111+ CHANGED_PACKAGES : ${{ needs.detect-changes.outputs.packages }}
102112 run : |
103- export PATH="$HOME/.xlings/subos/current/bin:$PATH"
104- xmake build -P . -y templates_test
105- xmake run -P . templates_test
106- - name : templates (windows)
107- if : runner.os == 'Windows' && needs.detect-changes.outputs.templates == 'true'
108- working-directory : tests
109- run : |
110- $env:PATH = "$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
111- xmake build -P . -y templates_test
112- xmake run -P . templates_test
113+ TARGETS=""
114+ if [ "$BUILD_ALL" = "true" ]; then
115+ TARGETS=$(grep -r 'target("' */*/xmake.lua 2>/dev/null | sed 's/.*target("\([^"]*\)").*/\1/' | sort -u)
116+ echo "Building ALL targets: $TARGETS"
117+ else
118+ for pkg in $(echo "$CHANGED_PACKAGES" | tr -d '[]"' | tr ',' ' '); do
119+ TARGET="${pkg}_test"
120+ if grep -rq "target(\"$TARGET\")" */*/xmake.lua 2>/dev/null; then
121+ TARGETS="$TARGETS $TARGET"
122+ else
123+ echo "Warning: no test target '$TARGET' found for package '$pkg', skipping"
124+ fi
125+ done
126+ echo "Building changed targets: $TARGETS"
127+ fi
113128
114- # cmdline
115- - name : cmdline (unix)
116- if : runner.os != 'Windows' && needs.detect-changes.outputs.cmdline == 'true'
117- working-directory : tests
118- run : |
119- export PATH="$HOME/.xlings/subos/current/bin:$PATH"
120- xmake build -P . -y cmdline_test
121- xmake run -P . cmdline_test test_input
122- - name : cmdline (windows)
123- if : runner.os == 'Windows' && needs.detect-changes.outputs.cmdline == 'true'
124- working-directory : tests
125- run : |
126- $env:PATH = "$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
127- xmake build -P . -y cmdline_test
128- xmake run -P . cmdline_test test_input
129+ # llmapi_test: skip entirely — requires API key to run, and xmake's
130+ # C++23 module system can't resolve transitive module deps from
131+ # installed packages. Tested in llmapi's own repo CI instead.
132+ TARGETS=$(echo "$TARGETS" | tr ' ' '\n' | grep -v llmapi_test | tr '\n' ' ')
129133
130- # llmapi (build only, needs API key to run)
131- - name : llmapi (unix)
132- if : runner.os != 'Windows' && needs.detect-changes.outputs.llmapi == 'true'
133- working-directory : tests
134- run : |
135- export PATH="$HOME/.xlings/subos/current/bin:$PATH"
136- xmake build -P . -y llmapi_test
137- - name : llmapi (windows)
138- if : runner.os == 'Windows' && needs.detect-changes.outputs.llmapi == 'true'
139- working-directory : tests
140- run : |
141- $env:PATH = "$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
142- xmake build -P . -y llmapi_test
134+ FAILED=""
135+ for target in $TARGETS; do
136+ echo "=== Building $target ==="
137+ if ! xmake build -P . -y "$target"; then
138+ echo "FAILED to build $target"
139+ FAILED="$FAILED $target"
140+ continue
141+ fi
143142
144- # lua
145- - name : lua (unix)
146- if : runner.os != 'Windows' && needs.detect-changes.outputs.lua == 'true'
147- working-directory : tests
148- run : |
149- export PATH="$HOME/.xlings/subos/current/bin:$PATH"
150- xmake build -P . -y lua_test
151- xmake run -P . lua_test
152- - name : lua (windows)
153- if : runner.os == 'Windows' && needs.detect-changes.outputs.lua == 'true'
154- working-directory : tests
155- run : |
156- $env:PATH = "$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
157- xmake build -P . -y lua_test
158- xmake run -P . lua_test
143+ echo "=== Running $target ==="
144+ if [ "$target" = "cmdline_test" ]; then
145+ xmake run -P . "$target" test_input
146+ else
147+ xmake run -P . "$target"
148+ fi
149+ done
159150
160- # mcpplibs-xpkg
161- - name : mcpplibs-xpkg (unix)
162- if : runner.os != 'Windows' && needs.detect-changes.outputs.xpkg == 'true'
163- working-directory : tests
164- run : |
165- export PATH="$HOME/.xlings/subos/current/bin:$PATH"
166- xmake build -P . -y mcpplibs-xpkg_test
167- xmake run -P . mcpplibs-xpkg_test
168- - name : mcpplibs-xpkg (windows)
169- if : runner.os == 'Windows' && needs.detect-changes.outputs.xpkg == 'true'
151+ if [ -n "$FAILED" ]; then
152+ echo "::error::Failed targets:$FAILED"
153+ exit 1
154+ fi
155+
156+ - name : Build and test (windows)
157+ if : runner.os == 'Windows'
170158 working-directory : tests
159+ env :
160+ BUILD_ALL : ${{ needs.detect-changes.outputs.build_all }}
161+ CHANGED_PACKAGES : ${{ needs.detect-changes.outputs.packages }}
171162 run : |
172- $env:PATH = "$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
173- xmake build -P . -y mcpplibs-xpkg_test
174- xmake run -P . mcpplibs-xpkg_test
163+ $targets = @()
164+ if ($env:BUILD_ALL -eq "true") {
165+ $targets = Get-ChildItem -Recurse -Filter "xmake.lua" -Path "*/*" |
166+ Select-String 'target\("([^"]+)"\)' |
167+ ForEach-Object { $_.Matches[0].Groups[1].Value } |
168+ Sort-Object -Unique
169+ Write-Host "Building ALL targets: $($targets -join ', ')"
170+ } else {
171+ $packages = $env:CHANGED_PACKAGES | ConvertFrom-Json
172+ foreach ($pkg in $packages) {
173+ $target = "${pkg}_test"
174+ $found = Get-ChildItem -Recurse -Filter "xmake.lua" -Path "*/*" |
175+ Select-String "target\(`"$target`"\)" -Quiet
176+ if ($found) {
177+ $targets += $target
178+ } else {
179+ Write-Host "Warning: no test target '$target' found for package '$pkg', skipping"
180+ }
181+ }
182+ Write-Host "Building changed targets: $($targets -join ', ')"
183+ }
184+
185+ # Skip llmapi_test (same reason as unix)
186+ $targets = $targets | Where-Object { $_ -ne "llmapi_test" }
187+
188+ $failed = @()
189+ foreach ($target in $targets) {
190+ Write-Host "=== Building $target ==="
191+ xmake build -P . -y $target
192+ if ($LASTEXITCODE -ne 0) {
193+ Write-Host "FAILED to build $target"
194+ $failed += $target
195+ continue
196+ }
197+
198+ Write-Host "=== Running $target ==="
199+ if ($target -eq "cmdline_test") {
200+ xmake run -P . $target test_input
201+ } else {
202+ xmake run -P . $target
203+ }
204+ }
205+
206+ if ($failed.Count -gt 0) {
207+ Write-Host "::error::Failed targets: $($failed -join ', ')"
208+ exit 1
209+ }
0 commit comments