Skip to content

Commit 1d7f67e

Browse files
committed
fix(ci): use .xlings.json for dependency management and dynamic package detection
- Add .xlings.json declaring xmake, gcc (linux), llvm (macos) as project deps - Simplify setup-toolchain action: use curl instead of gh API (no token needed), then run `xlings install` to install all deps from .xlings.json - Replace hardcoded package filters with dynamic git-diff-based detection - When CI config changes, automatically build all test targets - Consolidate per-package build steps into a single loop
1 parent 77857ab commit 1d7f67e

3 files changed

Lines changed: 165 additions & 131 deletions

File tree

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,50 @@
11
name: Setup C++23 Toolchain
2-
description: Install xlings and C++23 toolchain for the current platform
2+
description: Install xlings and project dependencies declared in .xlings.json
33

44
runs:
55
using: composite
66
steps:
7-
- name: Setup (linux)
7+
- name: Install xlings (linux)
88
if: runner.os == 'Linux'
99
shell: bash
1010
run: |
11-
LATEST_VERSION=$(gh release view --repo d2learn/xlings --json tagName -q '.tagName')
11+
LATEST_VERSION=$(curl -s https://api.github.com/repos/d2learn/xlings/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/')
1212
VERSION_NUM=${LATEST_VERSION#v}
1313
TARBALL="xlings-${VERSION_NUM}-linux-x86_64.tar.gz"
14-
gh release download "$LATEST_VERSION" --repo d2learn/xlings --pattern "$TARBALL" --dir /tmp
14+
curl -sL "https://github.com/d2learn/xlings/releases/download/${LATEST_VERSION}/${TARBALL}" -o "/tmp/${TARBALL}"
1515
mkdir -p "$HOME/.xlings"
16-
tar -xzf "/tmp/$TARBALL" -C "$HOME/.xlings" --strip-components=1
16+
tar -xzf "/tmp/${TARBALL}" -C "$HOME/.xlings" --strip-components=1
1717
"$HOME/.xlings/bin/xlings" self install
18-
env:
19-
GH_TOKEN: ${{ github.token }}
2018
21-
- name: Setup (macos)
19+
- name: Install xlings (macos)
2220
if: runner.os == 'macOS'
2321
shell: bash
2422
run: |
25-
LATEST_VERSION=$(gh release view --repo d2learn/xlings --json tagName -q '.tagName')
23+
LATEST_VERSION=$(curl -s https://api.github.com/repos/d2learn/xlings/releases/latest | grep '"tag_name"' | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/')
2624
VERSION_NUM=${LATEST_VERSION#v}
2725
ARCH=$(uname -m)
2826
TARBALL="xlings-${VERSION_NUM}-macosx-${ARCH}.tar.gz"
29-
gh release download "$LATEST_VERSION" --repo d2learn/xlings --pattern "$TARBALL" --dir /tmp
27+
curl -sL "https://github.com/d2learn/xlings/releases/download/${LATEST_VERSION}/${TARBALL}" -o "/tmp/${TARBALL}"
3028
mkdir -p "$HOME/.xlings"
31-
tar -xzf "/tmp/$TARBALL" -C "$HOME/.xlings" --strip-components=1
29+
tar -xzf "/tmp/${TARBALL}" -C "$HOME/.xlings" --strip-components=1
3230
xattr -dr com.apple.quarantine "$HOME/.xlings" 2>/dev/null || true
3331
"$HOME/.xlings/bin/xlings" self install
34-
env:
35-
GH_TOKEN: ${{ github.token }}
3632
37-
- name: Setup (windows)
33+
- name: Install xlings (windows)
3834
if: runner.os == 'Windows'
3935
shell: pwsh
4036
run: irm https://raw.githubusercontent.com/d2learn/xlings/refs/heads/main/tools/other/quick_install.ps1 | iex
4137

42-
- name: Install toolchain (linux)
43-
if: runner.os == 'Linux'
38+
- name: Install dependencies (unix)
39+
if: runner.os != 'Windows'
4440
shell: bash
4541
run: |
46-
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
47-
xlings install gcc@15 -y
42+
export PATH="$HOME/.xlings/bin:$HOME/.xlings/subos/current/bin:$PATH"
43+
xlings install -y
4844
49-
- name: Install toolchain (macos)
50-
if: runner.os == 'macOS'
51-
shell: bash
45+
- name: Install dependencies (windows)
46+
if: runner.os == 'Windows'
47+
shell: pwsh
5248
run: |
53-
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
54-
xlings install llvm@20 -y
49+
$env:PATH = "$env:USERPROFILE\.xlings\bin;$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
50+
xlings install -y

.github/workflows/ci.yml

Lines changed: 139 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ on:
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

1719
env:
1820
XLINGS_NON_INTERACTIVE: 1
@@ -21,46 +23,56 @@ 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+
# Determine the base ref for comparison
37+
if [ "${{ github.event_name }}" = "pull_request" ]; then
38+
BASE="${{ github.event.pull_request.base.sha }}"
39+
else
40+
BASE="${{ github.event.before }}"
41+
fi
42+
43+
# Check if CI config itself changed — if so, build all
44+
CI_CHANGED=$(git diff --name-only "$BASE" HEAD -- .github/ .xlings.json || true)
45+
if [ -n "$CI_CHANGED" ]; then
46+
echo "build_all=true" >> "$GITHUB_OUTPUT"
47+
echo "CI config changed, will build all packages"
48+
else
49+
echo "build_all=false" >> "$GITHUB_OUTPUT"
50+
fi
51+
52+
# Collect changed package names from packages/ and tests/ directories
53+
CHANGED_DIRS=$(git diff --name-only "$BASE" HEAD -- packages/ tests/ | \
54+
sed -n 's|^packages/./\([^/]*\)/.*|\1|p; s|^tests/./\([^/]*\)/.*|\1|p' | \
55+
sort -u)
56+
57+
# Build JSON array of changed packages
58+
JSON="["
59+
FIRST=true
60+
for pkg in $CHANGED_DIRS; do
61+
if [ "$FIRST" = true ]; then
62+
FIRST=false
63+
else
64+
JSON="$JSON,"
65+
fi
66+
JSON="$JSON\"$pkg\""
67+
done
68+
JSON="$JSON]"
69+
70+
echo "packages=$JSON" >> "$GITHUB_OUTPUT"
71+
echo "Changed packages: $JSON"
5572
5673
build:
5774
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'
75+
if: needs.detect-changes.outputs.build_all == 'true' || needs.detect-changes.outputs.packages != '[]'
6476
strategy:
6577
fail-fast: false
6678
matrix:
@@ -78,97 +90,116 @@ jobs:
7890
if: runner.os == 'Linux'
7991
working-directory: tests
8092
run: |
81-
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
93+
export PATH="$HOME/.xlings/bin:$HOME/.xlings/subos/current/bin:$PATH"
8294
xmake f -P . -y
8395
8496
- name: Configure (macos)
8597
if: runner.os == 'macOS'
8698
working-directory: tests
8799
run: |
88-
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
100+
export PATH="$HOME/.xlings/bin:$HOME/.xlings/subos/current/bin:$PATH"
89101
xmake f -P . -y --toolchain=llvm
90102
91103
- name: Configure (windows)
92104
if: runner.os == 'Windows'
93105
working-directory: tests
94106
run: |
95-
$env:PATH = "$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
107+
$env:PATH = "$env:USERPROFILE\.xlings\bin;$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
96108
xmake f -P . -y
97109
98-
# templates
99-
- name: templates (unix)
100-
if: runner.os != 'Windows' && needs.detect-changes.outputs.templates == 'true'
101-
working-directory: tests
102-
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'
110+
- name: Build and test (unix)
111+
if: runner.os != 'Windows'
108112
working-directory: tests
113+
env:
114+
BUILD_ALL: ${{ needs.detect-changes.outputs.build_all }}
115+
CHANGED_PACKAGES: ${{ needs.detect-changes.outputs.packages }}
109116
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
117+
export PATH="$HOME/.xlings/bin:$HOME/.xlings/subos/current/bin:$PATH"
113118
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
119+
# Collect test targets to build
120+
TARGETS=""
121+
if [ "$BUILD_ALL" = "true" ]; then
122+
# Find all test targets from test xmake.lua files
123+
TARGETS=$(grep -r 'target("' */*/xmake.lua 2>/dev/null | sed 's/.*target("\([^"]*\)").*/\1/' | sort -u)
124+
echo "Building ALL targets: $TARGETS"
125+
else
126+
# Build only changed packages
127+
for pkg in $(echo "$CHANGED_PACKAGES" | tr -d '[]"' | tr ',' ' '); do
128+
TARGET="${pkg}_test"
129+
# Verify the target exists
130+
if grep -rq "target(\"$TARGET\")" */*/xmake.lua 2>/dev/null; then
131+
TARGETS="$TARGETS $TARGET"
132+
else
133+
echo "Warning: no test target '$TARGET' found for package '$pkg', skipping"
134+
fi
135+
done
136+
echo "Building changed targets: $TARGETS"
137+
fi
129138
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
139+
# Build and run each target
140+
for target in $TARGETS; do
141+
echo "=== Building $target ==="
142+
xmake build -P . -y "$target"
143143
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
144+
# llmapi requires API key, only build don't run
145+
if [ "$target" = "llmapi_test" ]; then
146+
echo "Skipping run for $target (requires API key)"
147+
continue
148+
fi
159149
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'
150+
echo "=== Running $target ==="
151+
if [ "$target" = "cmdline_test" ]; then
152+
xmake run -P . "$target" test_input
153+
else
154+
xmake run -P . "$target"
155+
fi
156+
done
157+
158+
- name: Build and test (windows)
159+
if: runner.os == 'Windows'
170160
working-directory: tests
161+
env:
162+
BUILD_ALL: ${{ needs.detect-changes.outputs.build_all }}
163+
CHANGED_PACKAGES: ${{ needs.detect-changes.outputs.packages }}
171164
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
165+
$env:PATH = "$env:USERPROFILE\.xlings\bin;$env:USERPROFILE\.xlings\subos\current\bin;$env:PATH"
166+
167+
# Collect test targets
168+
$targets = @()
169+
if ($env:BUILD_ALL -eq "true") {
170+
$targets = Get-ChildItem -Recurse -Filter "xmake.lua" -Path "*/*" |
171+
Select-String 'target\("([^"]+)"\)' |
172+
ForEach-Object { $_.Matches[0].Groups[1].Value } |
173+
Sort-Object -Unique
174+
Write-Host "Building ALL targets: $($targets -join ', ')"
175+
} else {
176+
$packages = $env:CHANGED_PACKAGES | ConvertFrom-Json
177+
foreach ($pkg in $packages) {
178+
$target = "${pkg}_test"
179+
$found = Get-ChildItem -Recurse -Filter "xmake.lua" -Path "*/*" |
180+
Select-String "target\(`"$target`"\)" -Quiet
181+
if ($found) {
182+
$targets += $target
183+
} else {
184+
Write-Host "Warning: no test target '$target' found for package '$pkg', skipping"
185+
}
186+
}
187+
Write-Host "Building changed targets: $($targets -join ', ')"
188+
}
189+
190+
foreach ($target in $targets) {
191+
Write-Host "=== Building $target ==="
192+
xmake build -P . -y $target
193+
194+
if ($target -eq "llmapi_test") {
195+
Write-Host "Skipping run for $target (requires API key)"
196+
continue
197+
}
198+
199+
Write-Host "=== Running $target ==="
200+
if ($target -eq "cmdline_test") {
201+
xmake run -P . $target test_input
202+
} else {
203+
xmake run -P . $target
204+
}
205+
}

.xlings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"workspace": {
3+
"xmake": "3.0.7",
4+
"gcc": { "linux": "15.1.0" },
5+
"llvm": { "macosx": "20.1.7" }
6+
}
7+
}

0 commit comments

Comments
 (0)