Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/new
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ cp -R "$SRC/." "$DEST/"

# Substitute __NAME__ in the files that carry it. Portable in-place edit
# (BSD and GNU sed disagree on -i), so go through a temp file.
for f in package.json package-lock.json README.md; do
for f in package.json README.md; do
[ -f "$DEST/$f" ] || continue
tmp="$DEST/$f.tmp.$$"
sed "s/__NAME__/$NAME/g" "$DEST/$f" >"$tmp"
Expand Down
51 changes: 37 additions & 14 deletions template/.github/workflows/kernel-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ name: kernel-matrix
# QEMU/KVM on the runner. Each job writes a detail table to its step summary and
# uploads its result; the final `matrix` job pivots them into one ✅/❌ grid.
#
# Tune `matrix.kernel` to the kernels your script must support. Available tags
# live at https://quay.io/repository/lvh-images/kind?tab=tags — the `<ver>-main`
# entries track the latest build of each line. The example probes are CO-RE
# tracepoint programs, so they need a BTF-capable kernel (~5.4+); a program that
# uses newer features (ringbuf, sched_ext, …) will legitimately fail to load on
# kernels that predate them — which is exactly what this matrix surfaces.
# Tune `matrix.kernel` to the kernel lines your script must support (`6.6`,
# `bpf-next`, …); available lines live at
# https://quay.io/repository/lvh-images/kind?tab=tags. Each line is resolved to
# a concrete image at run time rather than using the floating `<ver>-main` tag,
# which the action can't consume: little-vm-helper@v0.0.30 derives the VM image
# filename by stripping a trailing *numeric* build stamp, so a `-main` tag
# yields a name that doesn't match the file `lvh` actually unpacks and the run
# dies with "invalid reference format". So each job looks up the newest
# date-stamped tag (`<ver>-YYYYMMDD.HHMMSS`, which the action handles) — always
# tracking the latest build, with no tag to bump and immune to quay's pruning of
# old stamps. The example probes are CO-RE tracepoint programs, so they need a
# BTF-capable kernel (~5.4+); a program that uses newer features (ringbuf,
# sched_ext, …) will legitimately fail to load on kernels that predate them —
# which is exactly what this matrix surfaces.

on:
workflow_dispatch:
Expand All @@ -29,17 +37,32 @@ jobs:
strategy:
fail-fast: false
matrix:
# Kernel lines to verify. Each is resolved to its newest date-stamped
# lvh image at run time (see the header).
kernel:
- '5.10-main'
- '5.15-main'
- '6.1-main'
- '6.6-main'
- '6.12-main'
- 'bpf-next-main'
- '6.1'
- '6.6'
- '6.12'
- 'bpf-next'
name: kernel ${{ matrix.kernel }}
steps:
- uses: actions/checkout@v4

- name: Resolve newest lvh image tag
id: img
env:
KERNEL: ${{ matrix.kernel }}
run: |
set -euo pipefail
# Newest <line>-YYYYMMDD.HHMMSS tag (date-stamps sort
# lexicographically, so tail -1 is the most recent build).
newest="$(curl -sf "https://quay.io/api/v1/repository/lvh-images/kind/tag/?onlyActiveTags=true&limit=100&filter_tag_name=like:${KERNEL}-" \
| jq -r '.tags[].name' \
| grep -E "^${KERNEL}-[0-9]{8}\.[0-9]+$" | sort | tail -1)"
[ -n "$newest" ] || { echo "::error::no date-stamped tag found for kernel line '${KERNEL}'"; exit 1; }
echo "resolved ${KERNEL} -> ${newest}"
echo "tag=${newest}" >> "$GITHUB_OUTPUT"

- name: Build BPF object + stage veristat
run: |
set -euo pipefail
Expand All @@ -65,7 +88,7 @@ jobs:
with:
test-name: veristat-${{ matrix.kernel }}
image: kind
image-version: ${{ matrix.kernel }}
image-version: ${{ steps.img.outputs.tag }}
host-mount: ${{ github.workspace }}
install-dependencies: 'true'
cmd: |
Expand Down Expand Up @@ -140,7 +163,7 @@ jobs:
m = re.match(r"(\d+)\.(\d+)", k)
return (1, 0, 0) if not m else (0, int(m.group(1)), int(m.group(2)))
kernels.sort(key=keyf)
short = lambda k: k.replace("-main", "")
short = lambda k: re.sub(r"-(main|\d{8}\.\d+)$", "", k)

print("## 🐧 Kernel verification matrix\n")
if not progs:
Expand Down
30 changes: 13 additions & 17 deletions template/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# make — build everything (BPF objects + JS bundle)
# make bpf — compile bpf/*.bpf.c into bin/* only
# make veristat — load the built object with veristat (verifier check on this kernel)
# make bundle — resolve npm/jsr deps and bundle the JS entry
# make bundle — bundle the JS entry with esbuild
# make postgen — finalize a freshly generated project (git init)
# make clangd — write a local .clangd pointing at the resolved toolchain
# make clean — remove build artifacts
Expand All @@ -24,31 +24,27 @@
include build/toolchain.mk
include build/bpf.mk

NPM ?= npm

all: bpf bundle

# Bundle the entry with the vendored esbuild. esbuild inlines node_modules
# and honors tsconfig `paths` (so `@/` resolves at bundle time), while
# `yeet:*` builtins and `*.bpf.o` objects stay external. The bundle is
# written to src/index.jsx, which the entry ladder prefers over src/main.jsx
# — so once built, that is what runs. The .jsx extension keeps the bundle
# eligible for component auto-mount. Compiled BPF objects in bin/ are loaded
# by path at runtime, never imported, so they are not bundled.
# Bundle the entry with the vendored esbuild. esbuild honors tsconfig `paths`
# (so `@/` resolves at bundle time), while `yeet:*` builtins and `*.bpf.o`
# objects stay external. The bundle is written to src/index.jsx, which the
# entry ladder prefers over src/main.jsx — so once built, that is what runs.
# The .jsx extension keeps the bundle eligible for component auto-mount.
# Compiled BPF objects in bin/ are loaded by path at runtime, never imported,
# so they are not bundled.
#
# `npm install` still runs first: esbuild resolves the project's own
# dependencies out of node_modules when inlining them.
# The build needs no npm/node: the starter imports only `yeet:*` builtins and
# local `@/` modules, which esbuild resolves on its own. If you add third-party
# packages to package.json, install them into node_modules with the package
# manager of your choice — esbuild inlines whatever it finds there.
ESBUILD_FLAGS := --bundle --format=esm --platform=neutral \
--main-fields=module,main --conditions=import,module \
--outfile=src/index.jsx --jsx=automatic --jsx-import-source=yeet:tui

bundle: node_modules | toolchain
bundle: | toolchain
$(ESBUILD) src/main.jsx $(ESBUILD_FLAGS) '--external:yeet:*' '--external:*.bpf.o'

node_modules: package.json
$(NPM) install
@touch node_modules

# Post-generation finalize: initialize a git repository with the vendored git
# (fetched via `vendored-git`). Idempotent — skipped if this is already a repo.
# The scaffolders (`yeet new`, `scripts/new`) run `make postgen` after creating
Expand Down
25 changes: 17 additions & 8 deletions template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ shares its `control`; each probe module attaches its own maps.
Makefile build frontend — orchestrates the two compilers
build/bpf.mk clang + bpftool rules: src/bpf/*.bpf.c -> bin/probe.bpf.o
build/gen-vmlinux.sh generates src/bpf/include/vmlinux.h from kernel BTF
package.json esbuild bundle script + npm deps
package.json project manifest + optional npm/jsr deps
tsconfig.json `#/` -> project root, `@/` -> ./src path aliases
src/main.jsx entry — composition root: input + mount
src/probes/probe.js loads the shared BPF object (binds maps, start())
Expand Down Expand Up @@ -68,8 +68,10 @@ yeet run . # runs the bundled src/index.jsx (needs root for BPF)

`make` runs two independent compilers: **clang + bpftool** compile
`src/bpf/*.bpf.c` and link them into one loadable object `bin/probe.bpf.o`;
**esbuild** bundles `src/main.jsx` into `src/index.jsx`, inlining npm deps and
the `@/` alias and leaving `yeet:*` builtins external.
**esbuild** bundles `src/main.jsx` into `src/index.jsx`, resolving the `@/`
alias (and inlining any npm/jsr deps you add) and leaving `yeet:*` builtins
external. esbuild is vendored by the toolchain, so the build needs no
node/npm.

The data layer loads the object at runtime:

Expand Down Expand Up @@ -122,9 +124,14 @@ which is why the BPF object is located with `import.meta.dirname`.

## npm / jsr packages

Add dependencies to `package.json` and import them normally; esbuild inlines
them at bundle time. Only packages that run in bare V8 work — no Node builtins
(`fs`, `net`, …), and no `Intl` / `TextEncoder` / `TextDecoder`.
The starter needs no third-party packages — it imports only `yeet:*` builtins
and local `@/` modules, so `make` builds with no npm/node and no `node_modules`.

To pull in a dependency, add it to `package.json` and install it into
`node_modules` with whatever package manager you like (`npm`, `pnpm`, `bun`, …)
— esbuild inlines whatever it finds there at bundle time. Only packages that
run in bare V8 work: no Node builtins (`fs`, `net`, …), and no `Intl` /
`TextEncoder` / `TextDecoder`.

## Pure-JS scripts

Expand All @@ -135,5 +142,7 @@ feed the components from any source that exposes the same signals.

- `clang` and `bpftool` (for the BPF leg; `bpftool` generates
`src/bpf/include/vmlinux.h` from the host kernel, which needs `CONFIG_DEBUG_INFO_BTF`)
- `node` + `npm` at build time for esbuild (authoring only — not needed on
hosts that merely *run* the built project)

No node/npm is required: esbuild is vendored by the toolchain, and the starter
has no third-party deps. (You only need a package manager if you add npm/jsr
dependencies of your own — see *npm / jsr packages* above.)
3 changes: 2 additions & 1 deletion template/build/toolchain.lock
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ GIT_SHA256_aarch64=19c6dda22c811324649e6e4aa8c369a8d822463d61d794d0e23e72fb77b53

# esbuild — official static (Go) binary from the @esbuild/<platform> npm
# package, re-hosted on our "toolchain" release. CI records the binary
# checksum. Keep ESBUILD_VERSION in sync with template/package.json.
# checksum. This pin is the project's only esbuild version (the build vendors
# it; there is no npm install), so bump it here to upgrade.
ESBUILD_VERSION=0.28.1
ESBUILD_SHA256_x86_64=0c6588b092a2c291a72bab90659f3c9e0e25e0fe59c9ac12b4dae4d945e5548c
ESBUILD_SHA256_aarch64=51e829ba36f36be6d9aea6e329ddc4f9350302339b16aaca96a3cb97f64a8ebb
Expand Down
Loading
Loading