|
1 | | -# ═══════════════════════════════════════════════════════════════ |
2 | | -# EoS QEMU Sanity Test — triggered by THIS repo's build |
3 | | -# |
4 | | -# Version resolution: |
5 | | -# - THIS repo → current commit (trigger) |
6 | | -# - Siblings → latest Git tag (release), fallback to master |
7 | | -# |
8 | | -# THIS_REPO: eboot |
9 | | -# ═══════════════════════════════════════════════════════════════ |
10 | | - |
11 | | -name: QEMU Sanity Test |
12 | | - |
13 | | -on: |
14 | | - push: |
15 | | - branches: [master] |
16 | | - tags: ["v*"] |
17 | | - pull_request: |
18 | | - branches: [master] |
19 | | - release: |
20 | | - types: [published] |
21 | | - workflow_dispatch: |
22 | | - inputs: |
23 | | - eos_ref: |
24 | | - description: "eos version (tag/branch/sha, empty=latest release)" |
25 | | - default: "" |
26 | | - eni_ref: |
27 | | - description: "eni version" |
28 | | - default: "" |
29 | | - eai_ref: |
30 | | - description: "eai version" |
31 | | - default: "" |
32 | | - eipc_ref: |
33 | | - description: "eipc version" |
34 | | - default: "" |
35 | | - ebuild_ref: |
36 | | - description: "ebuild version" |
37 | | - default: "" |
38 | | - |
39 | | -permissions: |
40 | | - contents: read |
41 | | - |
42 | | -env: |
43 | | - ORG: ${{ github.repository_owner }} |
44 | | - BOOT_TIMEOUT: "60" |
45 | | - |
46 | | -jobs: |
47 | | - resolve-versions: |
48 | | - name: Resolve Package Versions |
49 | | - runs-on: ubuntu-latest |
50 | | - outputs: |
51 | | - eboot_ref: ${{ github.sha }} |
52 | | - eos_ref: ${{ steps.resolve.outputs.eos_ref }} |
53 | | - eni_ref: ${{ steps.resolve.outputs.eni_ref }} |
54 | | - eai_ref: ${{ steps.resolve.outputs.eai_ref }} |
55 | | - eipc_ref: ${{ steps.resolve.outputs.eipc_ref }} |
56 | | - ebuild_ref: ${{ steps.resolve.outputs.ebuild_ref }} |
57 | | - steps: |
58 | | - - name: Resolve latest tags |
59 | | - id: resolve |
60 | | - env: |
61 | | - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
62 | | - run: | |
63 | | - resolve_ref() { |
64 | | - local repo="$1" override="$2" |
65 | | - if [ -n "$override" ]; then echo "$override"; return; fi |
66 | | - TAG=$(gh release view --repo "${{ env.ORG }}/$repo" --json tagName -q '.tagName' 2>/dev/null || echo "") |
67 | | - if [ -n "$TAG" ]; then echo "$TAG"; else |
68 | | - TAG=$(gh api "repos/${{ env.ORG }}/$repo/tags" --jq '.[0].name' 2>/dev/null || echo "") |
69 | | - if [ -n "$TAG" ]; then echo "$TAG"; else echo "master"; fi |
70 | | - fi |
71 | | - } |
72 | | - EOS_REF=$(resolve_ref "eos" "${{ github.event.inputs.eos_ref }}") |
73 | | - ENI_REF=$(resolve_ref "eni" "${{ github.event.inputs.eni_ref }}") |
74 | | - EAI_REF=$(resolve_ref "eai" "${{ github.event.inputs.eai_ref }}") |
75 | | - EIPC_REF=$(resolve_ref "eipc" "${{ github.event.inputs.eipc_ref }}") |
76 | | - EBUILD_REF=$(resolve_ref "ebuild" "${{ github.event.inputs.ebuild_ref }}") |
77 | | - echo "eos_ref=$EOS_REF" >> $GITHUB_OUTPUT |
78 | | - echo "eni_ref=$ENI_REF" >> $GITHUB_OUTPUT |
79 | | - echo "eai_ref=$EAI_REF" >> $GITHUB_OUTPUT |
80 | | - echo "eipc_ref=$EIPC_REF" >> $GITHUB_OUTPUT |
81 | | - echo "ebuild_ref=$EBUILD_REF" >> $GITHUB_OUTPUT |
82 | | - echo "═══ Resolved Versions ═══" |
83 | | - echo " eboot : ${{ github.sha }} (THIS REPO)" |
84 | | - echo " eos : $EOS_REF" |
85 | | - echo " eni : $ENI_REF" |
86 | | - echo " eai : $EAI_REF" |
87 | | - echo " eipc : $EIPC_REF" |
88 | | - echo " ebuild : $EBUILD_REF" |
89 | | -
|
90 | | - build: |
91 | | - name: Build All Packages |
92 | | - needs: resolve-versions |
93 | | - runs-on: ubuntu-latest |
94 | | - steps: |
95 | | - - name: Checkout eboot (THIS REPO) |
96 | | - uses: actions/checkout@v4 |
97 | | - with: |
98 | | - path: eboot |
99 | | - - name: Checkout eos @ ${{ needs.resolve-versions.outputs.eos_ref }} |
100 | | - uses: actions/checkout@v4 |
101 | | - with: |
102 | | - repository: ${{ env.ORG }}/eos |
103 | | - ref: ${{ needs.resolve-versions.outputs.eos_ref }} |
104 | | - path: eos |
105 | | - continue-on-error: true |
106 | | - - name: Checkout eni @ ${{ needs.resolve-versions.outputs.eni_ref }} |
107 | | - uses: actions/checkout@v4 |
108 | | - with: |
109 | | - repository: ${{ env.ORG }}/eni |
110 | | - ref: ${{ needs.resolve-versions.outputs.eni_ref }} |
111 | | - path: eni |
112 | | - continue-on-error: true |
113 | | - - name: Checkout eai @ ${{ needs.resolve-versions.outputs.eai_ref }} |
114 | | - uses: actions/checkout@v4 |
115 | | - with: |
116 | | - repository: ${{ env.ORG }}/eai |
117 | | - ref: ${{ needs.resolve-versions.outputs.eai_ref }} |
118 | | - path: eai |
119 | | - continue-on-error: true |
120 | | - - name: Checkout eipc @ ${{ needs.resolve-versions.outputs.eipc_ref }} |
121 | | - uses: actions/checkout@v4 |
122 | | - with: |
123 | | - repository: ${{ env.ORG }}/eipc |
124 | | - ref: ${{ needs.resolve-versions.outputs.eipc_ref }} |
125 | | - path: eipc |
126 | | - continue-on-error: true |
127 | | - - name: Checkout ebuild @ ${{ needs.resolve-versions.outputs.ebuild_ref }} |
128 | | - uses: actions/checkout@v4 |
129 | | - with: |
130 | | - repository: ${{ env.ORG }}/ebuild |
131 | | - ref: ${{ needs.resolve-versions.outputs.ebuild_ref }} |
132 | | - path: ebuild |
133 | | - continue-on-error: true |
134 | | - - name: Version matrix |
135 | | - run: | |
136 | | - for repo in eos eboot eni eai eipc ebuild; do |
137 | | - if [ -d "$repo" ]; then |
138 | | - TAG=$(cd "$repo" && git describe --tags --always 2>/dev/null) |
139 | | - echo " ✅ $repo @ $TAG" |
140 | | - else echo " ❌ $repo"; fi |
141 | | - done |
142 | | - - name: Install tools |
143 | | - run: | |
144 | | - sudo apt-get update -qq |
145 | | - sudo apt-get install -y cmake ninja-build gcc g++ cpio |
146 | | - - name: Build eos |
147 | | - if: hashFiles('eos/CMakeLists.txt') != '' |
148 | | - run: cmake -B eos/build -S eos -G Ninja -DEOS_BUILD_TESTS=OFF && cmake --build eos/build |
149 | | - - name: Build eboot |
150 | | - run: cmake -B eboot/build -S eboot -G Ninja -DEBLDR_BUILD_TESTS=OFF && cmake --build eboot/build |
151 | | - - name: Build eni |
152 | | - if: hashFiles('eni/CMakeLists.txt') != '' |
153 | | - run: cmake -B eni/build -S eni -G Ninja -DENI_BUILD_TESTS=OFF && cmake --build eni/build |
154 | | - - name: Build eai |
155 | | - if: hashFiles('eai/CMakeLists.txt') != '' |
156 | | - run: cmake -B eai/build -S eai -G Ninja -DEAI_BUILD_TESTS=OFF && cmake --build eai/build |
157 | | - - name: Build EIPC C SDK |
158 | | - if: hashFiles('eipc/sdk/c/CMakeLists.txt') != '' |
159 | | - run: cmake -B eipc/sdk/c/build -S eipc/sdk/c -G Ninja && cmake --build eipc/sdk/c/build |
160 | | - - name: Build rootfs + initramfs |
161 | | - run: | |
162 | | - python3 << 'EOF' |
163 | | - import os, shutil |
164 | | - from pathlib import Path |
165 | | - rootfs = Path("build/rootfs") |
166 | | - for d in ["bin","sbin","usr/bin","usr/sbin","usr/lib","etc/init.d","var/log","tmp","dev","proc","sys","run","home","root","boot","lib","opt","mnt"]: |
167 | | - (rootfs / d).mkdir(parents=True, exist_ok=True) |
168 | | - rcs = rootfs / "etc/init.d/rcS" |
169 | | - rcs.write_text("#!/bin/sh\nmount -t proc proc /proc\nmount -t sysfs sysfs /sys\nmount -t devtmpfs devtmpfs /dev\nhostname eos\necho 'EoS booted successfully'\nfor s in /etc/init.d/S*; do [ -x \"$s\" ] && \"$s\"; done\n") |
170 | | - os.chmod(str(rcs), 0o755) |
171 | | - test = rootfs / "etc/init.d/S99_qemu_test" |
172 | | - test.write_text("#!/bin/sh\necho '════ EoS QEMU Sanity ════'\ncat /etc/eos-release 2>/dev/null\necho \"Arch: $(uname -m)\"\nls /usr/lib/lib*.a 2>/dev/null | head -20 || true\necho 'TEST_PASSED=true'\npoweroff -f 2>/dev/null || reboot -f\n") |
173 | | - os.chmod(str(test), 0o755) |
174 | | - (rootfs/"etc/passwd").write_text("root:x:0:0:root:/root:/bin/sh\n") |
175 | | - (rootfs/"etc/group").write_text("root:x:0:\n") |
176 | | - (rootfs/"etc/hostname").write_text("eos-qemu\n") |
177 | | - (rootfs/"etc/eos-release").write_text("EOS_VERSION=0.3.0\nTRIGGER_REPO=eboot\n") |
178 | | - lib_dir = rootfs / "usr/lib" |
179 | | - for repo in ["eos","eboot","eni","eai"]: |
180 | | - bd = Path(repo) / "build" |
181 | | - if bd.exists(): |
182 | | - for lib in bd.rglob("*.a"): |
183 | | - shutil.copy2(str(lib), str(lib_dir / lib.name)) |
184 | | - EOF |
185 | | - cd build/rootfs && find . | cpio -o -H newc 2>/dev/null | gzip > ../initramfs.cpio.gz |
186 | | - - uses: actions/upload-artifact@v4 |
187 | | - with: |
188 | | - name: eos-sanity-image |
189 | | - path: build/initramfs.cpio.gz |
190 | | - retention-days: 3 |
191 | | - |
192 | | - qemu-boot: |
193 | | - name: QEMU ${{ matrix.arch }} |
194 | | - needs: build |
195 | | - runs-on: ubuntu-latest |
196 | | - strategy: |
197 | | - fail-fast: false |
198 | | - matrix: |
199 | | - include: |
200 | | - - arch: x86_64 |
201 | | - qemu_bin: qemu-system-x86_64 |
202 | | - qemu_args: "-machine q35 -cpu qemu64 -m 512 -nographic -no-reboot -serial stdio" |
203 | | - kernel_arg: '-append "console=ttyS0 root=/dev/ram0 init=/etc/init.d/rcS panic=5"' |
204 | | - pkg: qemu-system-x86 |
205 | | - - arch: aarch64 |
206 | | - qemu_bin: qemu-system-aarch64 |
207 | | - qemu_args: "-machine virt -cpu cortex-a57 -m 512 -nographic -no-reboot -serial stdio" |
208 | | - kernel_arg: '-append "console=ttyAMA0 root=/dev/ram0 init=/etc/init.d/rcS panic=5"' |
209 | | - pkg: qemu-system-arm |
210 | | - - arch: riscv64 |
211 | | - qemu_bin: qemu-system-riscv64 |
212 | | - qemu_args: "-machine virt -m 512 -nographic -no-reboot -serial stdio -bios none" |
213 | | - kernel_arg: '-append "console=ttyS0 root=/dev/ram0 init=/etc/init.d/rcS panic=5"' |
214 | | - pkg: qemu-system-misc |
215 | | - steps: |
216 | | - - uses: actions/download-artifact@v4 |
217 | | - with: |
218 | | - name: eos-sanity-image |
219 | | - path: build/ |
220 | | - - run: sudo apt-get update -qq && sudo apt-get install -y ${{ matrix.pkg }} |
221 | | - - name: Get kernel |
222 | | - run: | |
223 | | - if [ "${{ matrix.arch }}" = "x86_64" ]; then |
224 | | - KERN=$(find /boot -name "vmlinuz*" -type f 2>/dev/null | head -1) |
225 | | - [ -n "$KERN" ] && sudo cp "$KERN" build/vmlinuz || true |
226 | | - fi |
227 | | - continue-on-error: true |
228 | | - - name: Boot QEMU |
229 | | - timeout-minutes: 3 |
230 | | - run: | |
231 | | - if [ -f build/vmlinuz ]; then |
232 | | - timeout ${{ env.BOOT_TIMEOUT }} ${{ matrix.qemu_bin }} ${{ matrix.qemu_args }} \ |
233 | | - -kernel build/vmlinuz -initrd build/initramfs.cpio.gz \ |
234 | | - ${{ matrix.kernel_arg }} 2>&1 | tee build/boot.log || true |
235 | | - else |
236 | | - timeout 5 ${{ matrix.qemu_bin }} ${{ matrix.qemu_args }} \ |
237 | | - -kernel /dev/null 2>&1 | tee build/boot.log || true |
238 | | - fi |
239 | | - - name: Validate |
240 | | - run: | |
241 | | - grep -q "EoS booted successfully" build/boot.log 2>/dev/null && echo "✅ BOOT SUCCESS" || echo "⚠️ No boot marker" |
242 | | - grep -q "TEST_PASSED=true" build/boot.log 2>/dev/null && echo "✅ TESTS PASSED" || true |
243 | | - tail -20 build/boot.log |
244 | | - - if: always() |
245 | | - uses: actions/upload-artifact@v4 |
246 | | - with: |
247 | | - name: boot-log-${{ matrix.arch }} |
248 | | - path: build/boot.log |
249 | | - if-no-files-found: ignore |
250 | | - |
251 | | - eipc-tests: |
252 | | - name: EIPC Integration |
253 | | - needs: resolve-versions |
254 | | - runs-on: ubuntu-latest |
255 | | - steps: |
256 | | - - uses: actions/checkout@v4 |
257 | | - with: |
258 | | - repository: ${{ env.ORG }}/eipc |
259 | | - ref: ${{ needs.resolve-versions.outputs.eipc_ref }} |
260 | | - - uses: actions/setup-go@v5 |
261 | | - with: |
262 | | - go-version: "1.22" |
263 | | - - run: go test -race -v -count=1 ./... 2>&1 | tee results.txt |
264 | | - - run: | |
265 | | - FAIL_COUNT=$(grep -c '^--- FAIL' results.txt || true) |
266 | | - if [ "$FAIL_COUNT" != "" ] && [ "$FAIL_COUNT" -gt 0 ]; then exit 1; fi |
267 | | -
|
268 | | - sanity-gate: |
269 | | - name: Sanity Gate |
270 | | - if: always() |
271 | | - needs: [build, qemu-boot, eipc-tests] |
272 | | - runs-on: ubuntu-latest |
273 | | - steps: |
274 | | - - run: | |
275 | | - echo "═══ EoS QEMU Sanity — Triggered by: eboot ═══" |
276 | | - echo " Build: ${{ needs.build.result }}" |
277 | | - echo " QEMU: ${{ needs.qemu-boot.result }}" |
278 | | - echo " EIPC: ${{ needs.eipc-tests.result }}" |
279 | | - [ "${{ needs.build.result }}" = "success" ] || exit 1 |
280 | | - [ "${{ needs.eipc-tests.result }}" = "success" ] || exit 1 |
281 | | - echo "✅ All sanity checks passed" |
282 | | -# THIS_REPO: eboot |
0 commit comments