Files
systemd/.github/workflows/semcode.yml
T
Christian Brauner e091ac7ec9 ci: give the Claude review agents a semcode index of the kernel
The kernel tree is there so the agents can check kernel behavior
instead of relying on memory, but a question like which error codes an
ioctl can return still means grepping through ninety thousand files.
Indexing the kernel takes too long to do per review, so let's do it in
the semcode workflow once a week and cache the result next to the
binaries. Two analysis threads keep the indexer below nine gigabytes,
which a runner has; with four it peaks above eleven. The index is about
a gigabyte and tied to the semcode that wrote it, so it is looked up
under the commit of the restored binaries.

The index only answers for the commit it was built from. So when there
is one, the review fetches the kernel at that commit, which mainline
keeps, instead of cloning the latest. The fetch has depth two: at
startup semcode-mcp compares HEAD with its parent to see that the index
covers HEAD, and without the parent it checks every file instead. If
the fetch fails, the latest mainline is cloned as before and the index
stays unused. The agents get a second server, linux, next to systemd.
2026-11-05 12:29:43 +01:00

136 lines
5.4 KiB
YAML

# SPDX-License-Identifier: LGPL-2.1-or-later
# Builds semcode for the Claude review and indexes the Linux kernel with it.
#
# The review agents look up functions, types and call graphs with semcode
# instead of grepping through the trees. A cold build of semcode takes about
# twenty minutes and a kernel index longer than that, far more than a review
# may spend on them. Both go into the actions cache, from which the review job
# restores them:
#
# semcode-bin-<os>-<arch>-<semcode commit> the binaries
# semcode-linux-<os>-<arch>-<semcode commit>-<kernel commit> the kernel index
#
# The review restores the newest entry for the prefix it knows. The kernel
# index only answers for the commit it was built from, which the review
# therefore clones instead of the latest mainline. An index is tied to the
# semcode that wrote it, so a new SEMCODE_REF rebuilds both. A cache nobody
# restored for a week is evicted, and the kernel moves on, so this also runs
# weekly.
name: semcode
on:
push:
branches: [main]
paths: [.github/workflows/semcode.yml]
schedule:
# Mondays 03:00 UTC, before the European morning's reviews.
- cron: '0 3 * * 1'
workflow_dispatch:
concurrency:
group: semcode
permissions:
contents: read
env:
# masoncl/semcode-devel main plus its pull request 10, which lets semcode-mcp
# answer requests concurrently, from the fork the pull request comes from.
SEMCODE_REPOSITORY: https://github.com/brauner/semcode-devel.git
SEMCODE_REF: 08a0feefd4f97fce906420ee25961c44727efac8
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 60
# The review only runs in these two, so nobody else needs the caches.
if: contains(fromJSON('["systemd/systemd","daandemeyer/systemd"]'), github.repository)
outputs:
key: ${{ steps.key.outputs.key }}
steps:
- name: Compute the cache key
id: key
run: echo "key=semcode-bin-${RUNNER_OS}-${RUNNER_ARCH}-${SEMCODE_REF}" >>"$GITHUB_OUTPUT"
- name: Restore semcode
id: cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: ${{ runner.temp }}/semcode
key: ${{ steps.key.outputs.key }}
# protoc and the .proto files it reads: lance imports one of protobuf's
# well-known types, which ships in libprotobuf-dev and not with the
# compiler. gix reaches the network through curl built against openssl.
- name: Install build dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
protobuf-compiler libprotobuf-dev pkg-config libssl-dev libcurl4-openssl-dev
rustup update --no-self-update stable
- name: Build semcode
if: steps.cache.outputs.cache-hit != 'true'
run: |
src="$RUNNER_TEMP/semcode-src"
git init --quiet "$src"
git -C "$src" fetch --quiet --depth 1 "$SEMCODE_REPOSITORY" "$SEMCODE_REF"
git -C "$src" checkout --quiet --detach FETCH_HEAD
cargo build --manifest-path "$src/Cargo.toml" --release --locked --bin semcode-index --bin semcode-mcp
install -D -s -t "$RUNNER_TEMP/semcode/bin" "$src/target/release/semcode-index" "$src/target/release/semcode-mcp"
echo "$SEMCODE_REF" >"$RUNNER_TEMP/semcode/ref"
ls -l "$RUNNER_TEMP/semcode/bin"
linux:
runs-on: ubuntu-latest
needs: build
timeout-minutes: 120
steps:
- name: Restore semcode
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: ${{ runner.temp }}/semcode
key: ${{ needs.build.outputs.key }}
fail-on-cache-miss: true
# The review runs semcode in a bubblewrap sandbox and so does this, for
# one less difference between the two. Ubuntu restricts the unprivileged
# user namespaces it needs.
- name: Install the sandbox
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
sudo apt-get update && sudo apt-get install -y bubblewrap
- name: Clone the Linux kernel
id: linux
run: |
git clone --quiet --depth 1 --single-branch https://github.com/torvalds/linux.git "$RUNNER_TEMP/linux"
git -C "$RUNNER_TEMP/linux" log -1 --format="Cloned linux at %H (%s)"
echo "commit=$(git -C "$RUNNER_TEMP/linux" rev-parse HEAD)" >>"$GITHUB_OUTPUT"
# Two analysis threads keep the indexer below nine gigabytes, which a
# runner has; with four it peaks above eleven.
- name: Index the Linux kernel
env:
LINUX_COMMIT: ${{ steps.linux.outputs.commit }}
run: |
index="$RUNNER_TEMP/semcode-linux"
mkdir -p "$index"
bwrap --ro-bind / / --tmpfs /run --dev /dev --proc /proc --tmpfs /tmp --bind "$index" "$index" \
--clearenv --setenv PATH /usr/bin:/bin --setenv HOME "$HOME" \
--unshare-all --die-with-parent --new-session -- \
"$RUNNER_TEMP/semcode/bin/semcode-index" --jobs 2 --source "$RUNNER_TEMP/linux" --database "$index/.semcode.db"
echo "$LINUX_COMMIT" >"$index/commit"
du -sh "$index"
- name: Save the kernel index
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: ${{ runner.temp }}/semcode-linux
key: semcode-linux-${{ runner.os }}-${{ runner.arch }}-${{ env.SEMCODE_REF }}-${{ steps.linux.outputs.commit }}