Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
765c98c
Add baseline docs and rework workflow.
ericvh Apr 23, 2026
9e3d69f
Add Docker+QEMU test environment and align CI.
ericvh Apr 23, 2026
b543053
Split kernel build from QEMU test runs.
ericvh Apr 23, 2026
05111f9
Run v9fs tests in QEMU guest; align CI artifacts
ericvh Apr 24, 2026
da7b2a3
CI: run demand workflow on every push and manual dispatch
ericvh Apr 24, 2026
eb267c8
Fix CI: checkout v9fs/linux default branch ericvh/devel
ericvh Apr 24, 2026
f58856e
CI: pin v9fs/linux kernel checkout to main
ericvh Apr 24, 2026
62c1070
CI: fix permissions so logs upload as artifacts
ericvh Apr 24, 2026
bb79a53
CI: publish built kernel images to GHCR
ericvh Apr 24, 2026
7476e8c
CI: surface guest test failures via exit code
ericvh Apr 24, 2026
b6457a2
CI: include bzImage in kernel artifacts and GHCR package
ericvh Apr 24, 2026
e00378c
CI: default builds/tests on ARM64 runners
ericvh Apr 24, 2026
6e414a3
CI: propagate timestamp so guest exitcode is found
ericvh Apr 24, 2026
83b35a2
CI: publish arm64 Image as release asset
ericvh Apr 24, 2026
6bb9a63
Fix guest benchmarks: ship binaries and libs; always upload logs
ericvh Apr 24, 2026
87316d5
CI: dump QEMU logs to job output on failure
ericvh Apr 24, 2026
3d78527
Fix CI flake: ensure guest.exitcode is flushed
ericvh Apr 24, 2026
1d91532
CI: cache kernel build by v9fs/linux commit via GHCR
ericvh Apr 24, 2026
7585d09
Split kernel publish from harness CI workflows
ericvh Apr 24, 2026
09b4bc0
Archive rework take 1 under old/ and reset root
ericvh Apr 25, 2026
e66dbc4
Rebuild harness: guest-direct 9p smoke test + CI
ericvh Apr 25, 2026
b62937b
Kernel publish: enable 9p client config
ericvh Apr 25, 2026
b585d4a
Use v9fs/docker image instead of custom Dockerfile
ericvh Apr 25, 2026
8a6534c
Make harness scripts executable
ericvh Apr 25, 2026
6277b96
Run v9fs/docker container as root for mounts
ericvh Apr 25, 2026
3e45066
Initrd: locate busybox via PATH
ericvh Apr 25, 2026
f5e8d89
Initrd: build with u-root from v9fs/docker
ericvh Apr 25, 2026
afa43c9
Initrd: overlay /init onto v9fs/docker base initrd
ericvh Apr 25, 2026
69bed9d
Initrd overlay: keep base /bin/sh as default shell
ericvh Apr 25, 2026
9e16186
Initrd: replace /init in base initrd via Python newc
ericvh Apr 25, 2026
fa51653
Initrd rewrite: tolerate missing trailer EOF
ericvh Apr 25, 2026
ecdc307
Fix initrd overlay: never overwrite base initrd
ericvh Apr 25, 2026
8f43929
Use u-root busybox (bb) applets in initrd and guest
ericvh Apr 25, 2026
ecb4f22
Switch to u-root init + /uinit for guest run
ericvh Apr 25, 2026
83b3051
Install guest runner as /bin/uinit (u-root default)
ericvh Apr 25, 2026
79ab26a
uinit: use /bbin/bb, parse cmdline safely, no interactive shell
ericvh Apr 25, 2026
384ad18
Docs: add UROOT.md and CPU.md lookaside notes
ericvh Apr 25, 2026
08a650a
uinit: redirect stdout/stderr to /dev/console
ericvh Apr 25, 2026
0806e2e
Run guest tests via u-root cpu (cpud) over hostfwd
ericvh Apr 25, 2026
21c7ca5
cpu runner: use localhost for hostfwd target
ericvh Apr 25, 2026
562b0f6
Pin v9fs/docker:2.0.0; stream QEMU serial into docker logs
ericvh Apr 25, 2026
d1d6f9c
Revert docker image tag to latest
ericvh Apr 25, 2026
430c77a
QEMU foreground: use -nographic (no -serial stdio)
ericvh Apr 25, 2026
b9f26ed
Fix v9fs/docker paths: use /opt/v9fs initrd and cpu keys
ericvh Apr 25, 2026
69623b2
cpu connect: avoid localhost/IPv6 mismatch; show last error
ericvh Apr 25, 2026
a4e41a6
Daemonize QEMU; tail qemu.log for live output
ericvh Apr 25, 2026
e918988
cpud wait: probe cpu connectivity with timeouts + errors
ericvh Apr 25, 2026
ad7a775
cpud wait: show exit code vs timeout; enable cpu debug
ericvh Apr 25, 2026
c435e0f
cpud wait: add tcp port probe; force cpu to tcp4
ericvh Apr 25, 2026
13c6e50
cpud wait: log when port 17010 becomes reachable
ericvh Apr 25, 2026
0006361
Use cpu -ssh for liveness + test execution
ericvh Apr 25, 2026
79affa8
Smoke: guest-direct mount+ls over 9p (no cpu)
ericvh Apr 25, 2026
ab7f6a4
uinit: idle if poweroff fails (avoid init exit panic)
ericvh Apr 25, 2026
eb00e36
Initrd: inject mount/ls/sh and use custom /init for 9p ls
ericvh Apr 25, 2026
911c3cb
Fix ldd parsing in initrd builder
ericvh Apr 25, 2026
93c018c
Fix awk script separator in ldd parsing
ericvh Apr 25, 2026
ac84810
Fix awk regex for ldd library extraction
ericvh Apr 25, 2026
5518b62
Initrd rewrite: skip base entries we replace
ericvh Apr 25, 2026
326811e
Initrd: add directory entries for injected libs
ericvh Apr 25, 2026
d239b7e
Initrd: include core utils needed by /init
ericvh Apr 25, 2026
5ed72ec
Initrd: use sysrq for poweroff; don't require poweroff binary
ericvh Apr 25, 2026
9f69c74
Guest /init: create /proc,/sys,/dev before mounting
ericvh Apr 25, 2026
bc6d689
Guest /init: don't redirect to /dev/null before devtmpfs
ericvh Apr 25, 2026
72a274e
Smoke: write guest.exitcode to stable path
ericvh Apr 25, 2026
c96b3d9
Guest /init: write results under /mnt/9/test
ericvh Apr 25, 2026
efce5dd
Smoke init: start cpud in background
ericvh Apr 25, 2026
050a9df
9p export: share container root and chroot for smoke
ericvh Apr 25, 2026
f0da767
CI: add fsx/postmark/dbench stages (guest-direct chroot)
ericvh Apr 25, 2026
e83b20e
CI: add diod regression stage and docker cleanup
ericvh Apr 25, 2026
f22ea51
Merge origin/main into rework, preferring branch changes
Copilot Apr 25, 2026
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
87 changes: 87 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Harness CI

on:
push:
workflow_dispatch:
inputs:
kernel_release:
description: 'Release tag that provides arm64 Image (default: kernel-main)'
required: true
default: 'kernel-main'
type: string

permissions:
contents: read

jobs:
tests:
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
test:
- smoke
- fsx
- postmark
- dbench
- diod-regression
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KERNEL_RELEASE: ${{ inputs.kernel_release || 'kernel-main' }}
V9FS_DOCKER_IMAGE: ghcr.io/v9fs/docker:latest
steps:
- name: Checkout harness
uses: actions/checkout@v4

- name: Download published kernel Image
run: |
set -euo pipefail
mkdir -p kernel/.build/arch/arm64/boot
gh release download "${KERNEL_RELEASE}" -p Image -D kernel/.build/arch/arm64/boot --clobber
ls -la kernel/.build/arch/arm64/boot/Image

- name: Run ${{ matrix.test }} (guest-direct, chroot)
run: |
mkdir -p "$GITHUB_WORKSPACE/tmp"
docker run --rm --privileged \
--name "v9fs-test.${{ matrix.test }}.${{ github.run_id }}.${{ github.run_attempt }}" \
--label v9fs.harness=v9fs-test \
--user 0:0 \
-e KERNELBUILD=/workspaces/kernel/.build \
-v "$GITHUB_WORKSPACE:/home/v9fs-test/test" \
-v "$GITHUB_WORKSPACE/kernel:/workspaces/kernel" \
-v "$GITHUB_WORKSPACE/tmp:/workspaces/tmp" \
-w /home/v9fs-test/test \
"${V9FS_DOCKER_IMAGE}" \
bash -lc "./scripts/v9fs-run-tests '${{ matrix.test }}'"

- name: Dump logs on failure
if: failure()
run: |
set -euo pipefail
echo "==== logs tree ===="
(ls -R logs || true)
echo "==== qemu.log (tail) ===="
for f in logs/*/qemu.log; do
echo "---- $f ----"
tail -n 250 "$f" || true
done
echo "==== guest.log (tail) ===="
for f in logs/*/guest.log; do
echo "---- $f ----"
tail -n 250 "$f" || true
done

- name: Fix log permissions (for artifact upload)
if: always()
run: |
sudo chmod -R a+rX logs || true

- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.test }}-logs
path: logs
retention-days: 7

134 changes: 34 additions & 100 deletions .github/workflows/linux-kernel-publish.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
# Publish an arm64 `Image` built from v9fs/linux to this repo's GitHub Releases (+ GHCR).
#
# This repo cannot subscribe to pushes on another repo. Use one of:
# - `repository_dispatch` from v9fs/linux (recommended), or
# - `workflow_dispatch` here for manual / automation.
#
# Example repository_dispatch (from v9fs/linux CI or a PAT with repo scope on v9fs/test):
# event_type: publish-kernel-image
# client_payload:
# linux_repository: v9fs/linux
# linux_ref: main
# release_tag: kernel-main
# release_title: kernel-main # optional

name: Publish Linux kernel (arm64 Image)

on:
Expand All @@ -30,124 +16,72 @@ on:
default: 'main'
type: string
release_tag:
description: 'Release tag to publish/update (e.g. kernel-main, kernel-nightly, kernel-6.12.0)'
description: 'Release tag to publish/update (kernel-main, kernel-nightly, kernel-<version>)'
required: true
default: 'kernel-main'
type: string
release_title:
description: 'Optional GitHub release title (defaults to release_tag)'
required: false
default: ''
type: string

permissions:
contents: write
packages: write

jobs:
publish:
runs-on: ubuntu-24.04-arm
env:
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/v9fs-test-kernel
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
V9FS_DOCKER_IMAGE: ghcr.io/v9fs/docker:latest
steps:
- name: Checkout test harness (Dockerfile + scripts)
- name: Checkout harness (scripts)
uses: actions/checkout@v4

- name: Resolve parameters
id: params
run: |
set -euo pipefail
if [ "${GITHUB_EVENT_NAME}" = "repository_dispatch" ]; then
python3 -c "import json,os; ev=json.load(open(os.environ['GITHUB_EVENT_PATH'])); p=ev.get('client_payload') or {}; o=open(os.environ['GITHUB_OUTPUT'],'a',encoding='utf-8'); \
o.write('linux_repository=%s\n'%(p.get('linux_repository') or 'v9fs/linux')); \
o.write('linux_ref=%s\n'%(p.get('linux_ref') or 'main')); \
o.write('release_tag=%s\n'%(p.get('release_tag') or 'kernel-main')); \
o.write('release_title=%s\n'%(p.get('release_title') or p.get('release_tag') or 'kernel-main'))"
else
title="${{ inputs.release_title }}"
if [ -z "${title}" ]; then title="${{ inputs.release_tag }}"; fi
{
echo "linux_repository=${{ inputs.linux_repository }}"
echo "linux_ref=${{ inputs.linux_ref }}"
echo "release_tag=${{ inputs.release_tag }}"
echo "release_title=${title}"
} >>"$GITHUB_OUTPUT"
fi

- name: Checkout kernel under build
- name: Checkout kernel
uses: actions/checkout@v4
with:
repository: ${{ steps.params.outputs.linux_repository }}
ref: ${{ steps.params.outputs.linux_ref }}
repository: ${{ inputs.linux_repository || github.event.client_payload.linux_repository || 'v9fs/linux' }}
ref: ${{ inputs.linux_ref || github.event.client_payload.linux_ref || 'main' }}
path: linux

- name: Install ORAS
uses: oras-project/setup-oras@v1

- name: Determine kernel source SHA
id: kernelsha
run: |
set -euo pipefail
sha="$(git -C linux rev-parse HEAD)"
echo "sha=$sha" >>"$GITHUB_OUTPUT"
echo "KERNEL_SHA=$sha" >>"$GITHUB_ENV"
echo "Kernel SHA: $sha"

- name: Build Docker environment
run: docker build -t v9fs-test-env:local .

- name: Build kernel (QEMU guest kernel image)
- name: Build kernel (arm64 Image)
run: |
mkdir -p "$GITHUB_WORKSPACE/tmp" "$GITHUB_WORKSPACE/kernel"
docker run --rm --privileged \
--user 0:0 \
-v "$GITHUB_WORKSPACE:/home/v9fs-test/test" \
-v "$GITHUB_WORKSPACE/linux:/workspaces/linux" \
-v "$GITHUB_WORKSPACE/kernel:/workspaces/kernel" \
-v "$GITHUB_WORKSPACE/tmp:/workspaces/tmp" \
-w /home/v9fs-test/test \
v9fs-test-env:local \
bash -lc "v9fs-build-kernel && v9fs-export-kernel /workspaces/linux /workspaces/linux/.build /workspaces/kernel"
"${V9FS_DOCKER_IMAGE}" \
bash -lc '\
set -euo pipefail; \
out=/workspaces/linux/.build; \
mkdir -p \"$out\"; \
if [ ! -f \"$out/.config\" ]; then \
make -C /workspaces/linux O=\"$out\" defconfig; \
fi; \
cfg=/workspaces/linux/scripts/config; \
if [ -x \"$cfg\" ]; then \
\"$cfg\" --file \"$out/.config\" \
-e NET_9P -e NET_9P_VIRTIO -e 9P_FS -e 9P_FS_POSIX_ACL \
-e VIRTIO_PCI -e PCI; \
make -C /workspaces/linux O=\"$out\" olddefconfig; \
else \
echo \"WARNING: missing scripts/config; relying on defconfig\"; \
fi; \
make -C /workspaces/linux O=\"$out\" -j\"$(nproc)\"; \
mkdir -p /workspaces/kernel/.build/arch/arm64/boot; \
cp -f \"$out/arch/arm64/boot/Image\" /workspaces/kernel/.build/arch/arm64/boot/Image; \
'

- name: Publish arm64 Image as release asset
- name: Publish Image as release asset
env:
TAG: ${{ steps.params.outputs.release_tag }}
TITLE: ${{ steps.params.outputs.release_title }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.release_tag || github.event.client_payload.release_tag || 'kernel-main' }}
run: |
set -euo pipefail
img="kernel/.build/arch/arm64/boot/Image"
if [ ! -f "$img" ]; then
echo "ERROR: missing $img"
find kernel/.build -maxdepth 6 -type f | sed 's|^| |'
exit 2
fi

test -f "$img"
gh release view "$TAG" >/dev/null 2>&1 || \
gh release create "$TAG" --title "$TITLE" --notes "Automated arm64 kernel Image from ${GITHUB_REPOSITORY}@${GITHUB_SHA} (linux ${KERNEL_SHA})." --prerelease
gh release create "$TAG" --title "$TAG" --notes "Automated arm64 kernel Image." --prerelease
cp -f "$img" Image
gh release upload "$TAG" Image --clobber

- name: Package kernel image(s) for GHCR
run: |
set -euo pipefail
mkdir -p dist
mapfile -t files < <(
find kernel/.build/arch -type f \( -name '*Image' -o -name 'bzImage' \) -print | sort
)
if [ "${#files[@]}" -eq 0 ]; then
echo "ERROR: no kernel images found under kernel/.build/arch"
find kernel -maxdepth 5 -type f | sed 's|^| |'
exit 2
fi
tar -czf "dist/kernel-image.tar.gz" "${files[@]}"

- name: Publish kernel image package to GHCR (OCI artifact)
env:
ORAS_EXPERIMENTAL_OCI_ARTIFACT: "1"
run: |
set -euo pipefail
echo "${{ secrets.GITHUB_TOKEN }}" | oras login ghcr.io -u "${{ github.actor }}" --password-stdin

oras push "${GHCR_REPO}:linux-${KERNEL_SHA}" \
"dist/kernel-image.tar.gz:application/gzip"
oras tag "${GHCR_REPO}:linux-${KERNEL_SHA}" "${{ steps.params.outputs.release_tag }}"
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
logs/*
logs/
kernel/
tmp/
initrd.cpio
Expand Down
56 changes: 56 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# AGENTS.md

This file captures the working preferences for AI agents contributing to this repo.
Tweak freely.

## Branching and change hygiene

- Do active work on `rework` unless told otherwise.
- Keep `README.md`, `CHANGES.md`, and `TODO.md` updated as changes land.
- Do not commit generated outputs (`logs/`, `kernel/`, `tmp/`, `initrd.cpio`, pid files).

## Test philosophy
- Prefer **guest-direct execution** (Option A): run tests **inside the QEMU guest**.
- Avoid SSH/port-forwarding flows unless explicitly requested.
- use u-root based minimal initrd as root filesystem
- use u-root/cpu with NFS option to expose tools, benchmarks, tests, and results directories to the guest running in qemu
- be able to run as github actions or using act locally
- provide easy mechanism for running local tests (make or script based)
- verify workflow locally before pushing to github
- CI should surface failures (red dashboards) while still running the full suite:
- Run the whole matrix
- Record failures
- Exit non-zero at the end

## Local/dev environment assumptions

- Primary local dev is **macOS via Docker + QEMU**.
- Prefer solutions that work on Docker Desktop (no reliance on KVM).
- After any manual experiment that uses `docker run`, ensure containers are not left running:
- Prefer `docker run --rm` plus a project label `v9fs.harness=v9fs-test`.
- If you suspect a hung run left containers behind, clean up with `make docker-clean` or `./scripts/v9fs-docker-clean`.

## CI architecture preferences
- Use http://github.com/v9fs/docker published base image instead of building custom docker for kernel build and/or test frameworks
- Default to **ARM64** (`ubuntu-24.04-arm`) for builds/tests unless asked otherwise.
- Build and/or test will be triggered by external triggers (such as v9fs/linux changes) or user request in addition to any changes to this repo
- Separate concerns:
- **Kernel publishing** workflow: builds `v9fs/linux` arm64 `Image` and publishes it.
- **Harness CI** workflows: download a published kernel `Image` and run tests.
- Publishing:
- Prefer a stable, `wget`-able GitHub Release asset `Image` tagged `kernel-main`, `kernel-nightly`, or `kernel-<version>`.
- GHCR is optional/secondary; keep it consistent if used.

## Logging and debuggability

- Always preserve logs for failures (artifact upload `if: always()`).
- When tests fail, also dump the relevant tails into the CI console output:
- `logs/*/qemu.log`
- per-test `*.log`
- `guest.exitcode` markers (or equivalent)

## Style

- Prefer small, explicit scripts over complex magic.
- Keep paths stable and explicit (`/workspaces/share`, `kernel/.build/...`).
- Avoid large refactors unless requested; preserve working behavior first.
13 changes: 5 additions & 8 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
## Unreleased

- Start `rework` branch workflow and add baseline project docs (`README.md`, `CHANGES.md`, `TODO.md`).
- Add Docker + QEMU environment for local macOS runs and align GitHub Actions to use it.
- Run the suite **inside the QEMU guest** via initrd cmdline flags (`v9fs.run=1`, `v9fs.tests`, …) and a small guest runner (`scripts/v9fs-guest-run`), avoiding SSH/host port forwarding.
- Stabilize 9p exports for guest tests by bind-mounting the workspace/kernel/tmp/testbins under `/workspaces/share` (`scripts/v9fs-run-tests`).
- Give nightly/on-demand log artifacts **unique names** so parallel jobs do not collide on `actions/upload-artifact`.
- Ignore common local build outputs (`kernel/`, `tmp/`, generated initrd/pid files) in `.gitignore`.
- **Split workflows**: add `.github/workflows/linux-kernel-publish.yml` to build/publish **`v9fs/linux`** arm64 `Image` on **`repository_dispatch`** (from linux) or **`workflow_dispatch`**, publishing to **GitHub Releases** (`kernel-main`, `kernel-nightly`, `kernel-<version>`, …) plus **GHCR** (`linux-<sha>` + release tag).
- **Harness CI** (`.github/workflows/demand.yml`, `.github/workflows/nightly.yml`) no longer builds the kernel; they **`gh release download`** the published `Image` (defaults: `kernel-main` / `kernel-nightly`) and run QEMU tests on **ARM64** runners.
- Clean-slate rework started; prior implementation preserved under `old/rework-take-1/` (tag `rework-take-1`).
- Rebuild harness (take 2): minimal Docker+QEMU guest-direct smoke test that mounts a 9p export and validates basic filesystem operations.
- Add GitHub Actions:
- `linux-kernel-publish.yml` to build/publish arm64 `Image` as a release asset
- `ci.yml` to download `Image` and run the smoke harness in CI

45 changes: 45 additions & 0 deletions CPU.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# CPU.md

Lookaside notes for **u-root/cpu** (`cpu` + `cpud`) as it relates to this repo and the `v9fs/docker` initrd.

Per `AGENTS.md`, treat **source code** as the authority.

## What `cpud` is

`cpud` is the “daemon side” of a Plan9-inspired `cpu` session. It can run:

- **as PID 1** (init + daemon)
- **as a daemon** (listening server that forks per-session cpuds)
- **as a per-session “remote”** process

Source of truth:

- `u-root/cpu` `cmds/cpud/main_linux.go` and `cmds/cpud/cpuddoc.go`

## `cpud` flags (server mode)

From `cmds/cpud/main_linux.go` and `cmds/cpud/cpuddoc.go`:

- `-hk`: host key file (host key)
- `-pk`: public key file (default `key.pub`)
- `-sp`: listen port (default `17010`)
- `-net`: network (default `tcp`)
- `-d`: debug prints
- `-klog`: log to kernel log instead of stdout
- `-register` / `-registerTO`: optional controller registration
- `-sleepBeforeServing`: delay before serving (useful when running as init)

## `cpud` “remote” mode and argument rules

In remote mode (invoked as `cpud -remote ...`) the accepted flag set is intentionally smaller, and the program expects remaining args to be the command to run.

Source of truth:

- `cmds/cpud/main_linux.go` (`if len(os.Args)>1 && os.Args[1] == "-remote"...` and the custom FlagSet)

## Relevance to `v9fs/docker`

The `v9fs/docker` base initrd’s `/init` is a symlink to `bbin/cpud` (and `bbin/cpud` is an applet of the `bb` binary).

For this repo’s harness, we are **not** depending on `cpu/cpud` features to run tests yet; we only rely on the u-root init sequence and the availability of `/bbin/bb` applets to implement `/bin/uinit` and mount 9p.

Loading
Loading