Skip to content

Commit 293c366

Browse files
committed
Add PR validation job
Signed-off-by: Simon Davies <[email protected]>
1 parent fa31cb0 commit 293c366

File tree

4 files changed

+372
-1
lines changed

4 files changed

+372
-1
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
name: PR Validation
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
push:
7+
branches: [main]
8+
9+
env:
10+
# Consistent image tag for CI
11+
IMAGE_TAG: ci-${{ github.sha }}
12+
13+
jobs:
14+
lint:
15+
name: Lint
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Install just
22+
uses: extractions/setup-just@v2
23+
24+
- name: Setup Go
25+
uses: actions/setup-go@v5
26+
with:
27+
go-version: '1.25'
28+
cache-dependency-path: device-plugin/go.sum
29+
30+
- name: Setup Rust
31+
uses: dtolnay/rust-toolchain@stable
32+
with:
33+
components: rustfmt, clippy
34+
35+
- name: Check formatting
36+
run: just fmt-check
37+
38+
- name: Run linters
39+
run: just lint-strict
40+
41+
build:
42+
name: Build
43+
runs-on: ubuntu-latest
44+
steps:
45+
- name: Checkout
46+
uses: actions/checkout@v4
47+
48+
- name: Install just
49+
uses: extractions/setup-just@v2
50+
51+
- name: Setup Go
52+
uses: actions/setup-go@v5
53+
with:
54+
go-version: '1.25'
55+
cache-dependency-path: device-plugin/go.sum
56+
57+
- name: Build device plugin
58+
run: just plugin-build
59+
60+
integration:
61+
name: Integration Tests
62+
runs-on: ubuntu-24.04
63+
needs: [lint, build]
64+
steps:
65+
- name: Checkout
66+
uses: actions/checkout@v4
67+
68+
- name: Setup Go
69+
uses: actions/setup-go@v5
70+
with:
71+
go-version: '1.25'
72+
cache-dependency-path: device-plugin/go.sum
73+
74+
- name: Enable KVM
75+
run: |
76+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
77+
sudo udevadm control --reload-rules
78+
sudo udevadm trigger --name-match=kvm
79+
80+
# Verify KVM is available
81+
if [ ! -e /dev/kvm ]; then
82+
echo "::error::KVM is not available on this runner"
83+
exit 1
84+
fi
85+
ls -la /dev/kvm
86+
echo "✓ KVM enabled"
87+
88+
- name: Install just
89+
uses: extractions/setup-just@v2
90+
91+
- name: Install kind
92+
run: |
93+
go install sigs.k8s.io/kind@latest
94+
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
95+
96+
- name: Install kubectl
97+
uses: azure/setup-kubectl@v4
98+
99+
- name: Create KIND cluster
100+
run: |
101+
just local-up
102+
echo "✓ KIND cluster created"
103+
104+
- name: Build device plugin
105+
run: |
106+
just plugin-build
107+
echo "✓ Device plugin built"
108+
109+
- name: Push device plugin to local registry
110+
run: |
111+
just plugin-local-push
112+
echo "✓ Device plugin pushed to local registry"
113+
114+
- name: Deploy device plugin
115+
run: |
116+
just plugin-local-deploy
117+
echo "✓ Device plugin deployed"
118+
119+
- name: Wait for device plugin to be ready
120+
run: |
121+
echo "Waiting for device plugin DaemonSet..."
122+
kubectl rollout status daemonset/hyperlight-device-plugin -n hyperlight-system --timeout=120s
123+
124+
# Wait for node resources to be advertised
125+
echo "Waiting for node resources..."
126+
for i in {1..30}; do
127+
capacity=$(kubectl get nodes -o jsonpath='{.items[0].status.allocatable.hyperlight\.dev/hypervisor}' 2>/dev/null || echo "0")
128+
if [ "$capacity" != "0" ] && [ -n "$capacity" ]; then
129+
echo "✓ Node advertising $capacity hyperlight.dev/hypervisor devices"
130+
break
131+
fi
132+
echo "Waiting for device plugin to register resources... ($i/30)"
133+
sleep 2
134+
done
135+
136+
- name: Run device plugin tests
137+
run: |
138+
./scripts/test.sh plugin
139+
./scripts/test.sh nodes
140+
./scripts/test.sh kvm
141+
echo "✓ Device plugin tests passed"
142+
143+
- name: Build Hyperlight app
144+
run: |
145+
just app-build
146+
echo "✓ Hyperlight app built"
147+
148+
- name: Push Hyperlight app to local registry
149+
run: |
150+
just app-local-push
151+
echo "✓ Hyperlight app pushed to local registry"
152+
153+
- name: Deploy Hyperlight app
154+
run: |
155+
just app-local-deploy
156+
echo "✓ Hyperlight app deployed"
157+
158+
- name: Wait for Hyperlight app to be ready
159+
run: |
160+
echo "Waiting for Hyperlight app deployment..."
161+
kubectl rollout status deployment/hyperlight-hello-kvm --timeout=180s
162+
163+
- name: Validate Hyperlight app output
164+
run: |
165+
./scripts/test.sh app
166+
echo "✓ Hyperlight app tests passed"
167+
168+
- name: Show status
169+
if: always()
170+
run: |
171+
echo "=== Pods ==="
172+
kubectl get pods -A
173+
echo ""
174+
echo "=== Device Plugin Logs ==="
175+
kubectl logs -n hyperlight-system -l app.kubernetes.io/name=hyperlight-device-plugin --tail=50 || true
176+
echo ""
177+
echo "=== Hyperlight App Logs ==="
178+
kubectl logs -l app=hyperlight-hello --tail=50 || true
179+
180+
- name: Cleanup
181+
if: always()
182+
run: |
183+
just local-down || true

device-plugin/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const (
3838
serverSock = pluginapi.DevicePluginPath + "hyperlight.sock"
3939
kubeletSock = pluginapi.KubeletSocket
4040
cdiSpecPath = "/var/run/cdi/hyperlight.json"
41-
defaultDeviceCount = 2000 // Conservative default for MSHV; KVM can handle more
41+
defaultDeviceCount = 2000 // Conservative default for MSHV; KVM can handle more
4242
defaultDeviceUID = 65534 // Default UID for device node in container (nobody)
4343
defaultDeviceGID = 65534 // Default GID for device node in container (nobody)
4444
)

justfile

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,86 @@ app-undeploy:
279279
kubectl delete -f {{hyperlight_app_dir}}/k8s/deployment-kvm.yaml --ignore-not-found
280280
kubectl delete -f {{hyperlight_app_dir}}/k8s/deployment-mshv.yaml --ignore-not-found
281281

282+
# =============================================================================
283+
# CI / Testing
284+
# =============================================================================
285+
286+
# Run the full CI test suite locally (simulates PR validation workflow)
287+
ci-test:
288+
#!/usr/bin/env bash
289+
set -euo pipefail
290+
291+
echo "========================================"
292+
echo " Hyperlight CI Test Suite"
293+
echo "========================================"
294+
echo ""
295+
echo "This simulates the GitHub Actions PR validation workflow."
296+
echo ""
297+
298+
# Check KVM
299+
if [ ! -e /dev/kvm ]; then
300+
echo "❌ /dev/kvm not found - CI tests require KVM"
301+
exit 1
302+
fi
303+
echo "✓ KVM available"
304+
echo ""
305+
306+
# Step 1: Check formatting
307+
echo "=== Step 1/8: Check formatting ==="
308+
just fmt-check
309+
echo ""
310+
311+
# Step 2: Run linters
312+
echo "=== Step 2/8: Run linters ==="
313+
just lint-strict
314+
echo ""
315+
316+
# Step 3: Create KIND cluster
317+
echo "=== Step 3/8: Create KIND cluster ==="
318+
just local-up
319+
echo ""
320+
321+
# Step 4: Build device plugin
322+
echo "=== Step 4/8: Build device plugin ==="
323+
just plugin-build
324+
echo ""
325+
326+
# Step 5: Push and deploy device plugin
327+
echo "=== Step 5/8: Deploy device plugin ==="
328+
just plugin-local-push
329+
just plugin-local-deploy
330+
echo ""
331+
332+
# Step 6: Wait for device plugin and run tests
333+
echo "=== Step 6/8: Test device plugin ==="
334+
kubectl rollout status daemonset/hyperlight-device-plugin -n hyperlight-system --timeout=120s
335+
sleep 5 # Give kubelet time to update node resources
336+
{{project_root}}/scripts/test.sh plugin
337+
{{project_root}}/scripts/test.sh nodes
338+
{{project_root}}/scripts/test.sh kvm
339+
echo ""
340+
341+
# Step 7: Build Hyperlight app
342+
echo "=== Step 7/8: Build Hyperlight app ==="
343+
just app-build
344+
echo ""
345+
346+
# Step 8: Deploy and test Hyperlight app
347+
echo "=== Step 8/8: Deploy and test Hyperlight app ==="
348+
just app-local-push
349+
just app-local-deploy
350+
kubectl rollout status deployment/hyperlight-hello-kvm --timeout=180s
351+
{{project_root}}/scripts/test.sh app
352+
echo ""
353+
354+
echo "========================================"
355+
echo " ✓ All CI tests passed!"
356+
echo "========================================"
357+
358+
# Run the full CI test suite and cleanup afterwards
359+
ci-test-clean: ci-test
360+
just local-down
361+
282362
# =============================================================================
283363
# Development
284364
# =============================================================================
@@ -288,11 +368,41 @@ fmt:
288368
cd {{device_plugin_dir}} && go fmt ./...
289369
cd {{hyperlight_app_dir}} && cargo fmt --all
290370

371+
# Check formatting without modifying files (for CI)
372+
fmt-check:
373+
#!/usr/bin/env bash
374+
set -euo pipefail
375+
echo "Checking Go formatting..."
376+
cd {{device_plugin_dir}}
377+
if [ -n "$(gofmt -l .)" ]; then
378+
echo "❌ Go code is not formatted. Run 'just fmt'"
379+
gofmt -d .
380+
exit 1
381+
fi
382+
echo "✓ Go code formatted"
383+
384+
echo "Checking Rust formatting..."
385+
cd {{hyperlight_app_dir}}
386+
cargo fmt --all -- --check
387+
echo "✓ Rust code formatted"
388+
291389
# Run linters (Go + Rust host only - guest is no_std)
292390
lint:
293391
cd {{device_plugin_dir}} && go vet ./...
294392
cd {{hyperlight_app_dir}}/host && cargo clippy
295393

394+
# Run linters with warnings as errors (for CI)
395+
lint-strict:
396+
#!/usr/bin/env bash
397+
set -euo pipefail
398+
echo "Running Go vet..."
399+
cd {{device_plugin_dir}} && go vet ./...
400+
echo "✓ Go vet passed"
401+
402+
echo "Running Rust clippy with strict warnings..."
403+
cd {{hyperlight_app_dir}}/host && cargo clippy -- -D warnings
404+
echo "✓ Rust clippy passed"
405+
296406
# Check prerequisites
297407
check:
298408
#!/usr/bin/env bash

0 commit comments

Comments
 (0)