Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c384481
configs: split the monolithic defconfigs into merged fragment stacks
mcfbytes Sep 2, 2026
fe45fe8
scripts: fragment-stack checks (lockstep by construction, regen + gol…
mcfbytes Sep 2, 2026
faa5a1d
ci: consume the fragment stacks; add the lint-config job
mcfbytes Sep 2, 2026
591c8b3
docs: move every config comment into docs/buildroot-config.md; update…
mcfbytes Sep 2, 2026
7821802
docs: correct fragment symbol counts; note strace left the debug block
mcfbytes Sep 2, 2026
de65906
checks: host-independent, bump-tolerant golden; path-consumer guard; …
mcfbytes Sep 2, 2026
b370daf
check-config-fragments: keep lowercase BR2_ symbols in the golden hash
mcfbytes Sep 2, 2026
419b16a
de25: SD-card image definition + fail-closed checker (D2.4 desk half)
mcfbytes Sep 2, 2026
df3fc87
kernel: shared MiSTer driver fragment + minimal DE25 arm64 base config
mcfbytes Sep 2, 2026
3ff1017
de25: mainline U-Boot v2026.07 + TF-A v2.15.0 through Buildroot; card…
mcfbytes Sep 2, 2026
01545fe
docs(README): the rewritten loop= patch is aarch64-portable; the stoc…
mcfbytes Sep 2, 2026
5da42fa
de25 u-boot: SoCDK-validated SD PHY delays, default-speed first conta…
mcfbytes Sep 2, 2026
7856990
de25: card checker opens the FIT and allow-lists p1
mcfbytes Sep 2, 2026
b59b123
docs(de25): record wave 2 in the task list; refresh next steps
mcfbytes Sep 2, 2026
861f4a9
de25: follow the shared linux.hash to 7.2.3; re-verify the series and…
mcfbytes Sep 2, 2026
0a96b74
de25: build host kmod with xz so depmod can index the compressed modules
mcfbytes Sep 2, 2026
c943437
de25: DE25_ALLOW_NO_UBOOT skips the bl31/FIT asserts only when no FIT…
mcfbytes Sep 2, 2026
5ed5105
de25: fix the recipe continuation and add the srctree comment
mcfbytes Sep 2, 2026
2d0b32d
de25: close the if-block with a terminated echo
mcfbytes Sep 2, 2026
0e241d7
Merge pull request #138 from mcfbytes/feature/de25-wave2
mcfbytes Sep 2, 2026
6498f0a
Address Copilot review round 3 on #137
mcfbytes Sep 2, 2026
be645f7
docs(adr): 0029 D10 — the SMMU ships disabled; DMA isolation is a non…
mcfbytes Sep 2, 2026
76f3ab1
docs(de25-uboot): FIT reproducibility is per-tree [V], cross-tree [U]
mcfbytes Sep 2, 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
58 changes: 38 additions & 20 deletions .github/actions/buildroot-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ name: Build the MiSTer image (Buildroot)
description: >-
Prepares a GitHub-hosted runner for a Buildroot build (disk reclaim, apt
deps, five input caches), then builds the requested variant. main: configures
from the tracked defconfig and runs `make all` — linux.img + zImage_dtb in
output/images/. Any other name is a kernel-only variant (`make <name>` over
configs/mister_kernel_defconfig + configs/mister_<name>.fragment) — the
from the DE10-Nano fragment stack (configs/fragments/, stacks.mk) and runs
`make all` — linux.img + zImage_dtb in output/images/. Any other name is a
kernel-only variant (`make <name>` over the de10nano-kernel fragment stack
+ configs/mister_<name>.fragment) — the
variant kernel at output-<name>/images/zImage_dtb plus a depmod'd module
tree in output-<name>/target/.

Expand Down Expand Up @@ -162,10 +163,12 @@ runs:
echo "::error::unknown variant '$VARIANT' -- .github/actions/buildroot-build accepts 'main' or any kernel variant with a fragment at configs/mister_<name>.fragment (looked for configs/mister_${VARIANT}.fragment)" >&2
exit 1
fi
# Kernel variants build a COPY of main's toolchain/kernel stanzas
# (configs/mister_kernel_defconfig) — assert it hasn't drifted
# BEFORE any cache work, or a drift builds wrong under a cache key
# that then pins the wrong toolchain in. See docs/ci.md#variants.
# Kernel variants build on the de10nano-kernel fragment stack,
# which shares its toolchain/kernel fragments with main's stack by
# construction (configs/fragments/stacks.mk) — assert that
# structure still holds BEFORE any cache work, or a drift builds
# wrong under a cache key that then pins the wrong toolchain in.
# See docs/ci.md#variants.
scripts/check-kernel-defconfig-sync.sh
;;
esac
Expand All @@ -192,16 +195,29 @@ runs:
# first so editing one can't evict the cross-toolchain either. See
# docs/ci.md#toolchain-fingerprint before changing this filter.
#
# Fingerprints the variant's own base defconfig + fragment, not
# main's. See docs/ci.md#toolchain-fingerprint.
# Fingerprints the variant's own base STACK + fragment, not main's:
# main = the de10nano stack, a kernel variant = the de10nano-kernel
# stack (configs/fragments/stacks.mk, read through the same helper
# the check scripts use). The fragments are concatenated before the
# strip/filter/sort, so the residue is the same sorted set of lines
# the old single-file defconfig produced — the fingerprint, and
# therefore the cache key, did not move at the fragment split.
# See docs/ci.md#toolchain-fingerprint.
TC_GEN=1
ROOT="$PWD"
# shellcheck source=scripts/lib/config-stacks.sh
source scripts/lib/config-stacks.sh
if [ "$VARIANT" = "main" ]; then
fp_defconfig=configs/mister_de10nano_defconfig
mapfile -t fp_files < <(config_stack_files DE10NANO)
else
fp_defconfig=configs/mister_kernel_defconfig
mapfile -t fp_files < <(config_stack_files DE10NANO_KERNEL)
fi
if [ "${#fp_files[@]}" -eq 0 ]; then
echo "::error::configs/fragments/stacks.mk names no fragments for this variant's stack" >&2
exit 1
fi
sed -e 's/^[[:space:]]*#.*$//' -e 's/[[:space:]]\+#.*$//' -e 's/[[:space:]]*$//' \
"$fp_defconfig" \
"${fp_files[@]}" \
| grep -vE '^($|BR2_PACKAGE_|BR2_LINUX_KERNEL)' \
| sort > .br-toolchain-fingerprint

Expand Down Expand Up @@ -315,16 +331,18 @@ runs:

# 2. dl/ — every package source tarball. Lives at repo root (survives
# `make clean`) — NEVER move it under output/. Keyed on the full
# defconfig; restore-keys falls back to the version alone so an
# imperfect match still hydrates what didn't change. See
# docs/ci.md#dl-cache.
# DE10-Nano fragment stack (the three files configs/fragments/stacks.mk
# lists for DE10NANO_FRAGMENTS — hashFiles() cannot read stacks.mk, so
# they are spelled out here; keep the two in step); restore-keys falls
# back to the version alone so an imperfect match still hydrates what
# didn't change. See docs/ci.md#dl-cache.
- name: Restore Buildroot dl/ download cache
id: dl-cache
if: env.VARIANT == 'main'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: dl/
key: br-dl-${{ env.BUILDROOT_VERSION }}-${{ hashFiles('configs/mister_de10nano_defconfig') }}
key: br-dl-${{ env.BUILDROOT_VERSION }}-${{ hashFiles('configs/fragments/common.fragment', 'configs/fragments/de10nano.fragment', 'configs/fragments/de10nano-image.fragment') }}
restore-keys: |
br-dl-${{ env.BUILDROOT_VERSION }}-

Expand All @@ -339,7 +357,7 @@ runs:
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: dl/
key: br-dl-${{ env.VARIANT }}-${{ env.BUILDROOT_VERSION }}-${{ hashFiles('configs/mister_kernel_defconfig', format('configs/mister_{0}.fragment', env.VARIANT)) }}
key: br-dl-${{ env.VARIANT }}-${{ env.BUILDROOT_VERSION }}-${{ hashFiles('configs/fragments/common.fragment', 'configs/fragments/de10nano.fragment', 'configs/fragments/kernel-only.fragment', format('configs/mister_{0}.fragment', env.VARIANT)) }}
restore-keys: |
br-dl-${{ env.VARIANT }}-${{ env.BUILDROOT_VERSION }}-
br-dl-${{ env.BUILDROOT_VERSION }}-
Expand Down Expand Up @@ -435,14 +453,14 @@ runs:
run: make hostshim

# Regenerate output/.config UNCONDITIONALLY every run — a stale cached
# one can silently override a defconfig change (run 29293209070 died
# one can silently override a fragment change (run 29293209070 died
# here after a 52min stage 1). MAIN-ONLY: a kernel variant's .config is
# never cached, so it can't go stale in the first place. See
# docs/ci.md#configure-buildroot.
- name: Configure Buildroot (generate output/.config from the defconfig)
- name: Configure Buildroot (generate output/.config from the fragment stack)
if: env.VARIANT == 'main'
shell: bash
run: make mister_de10nano_defconfig
run: make de10nano-defconfig

# `make all` builds both stages under one ccache (BR2_CCACHE propagates
# through MAKEFLAGS to the musl initramfs too — distinct ccache keys, so
Expand Down
89 changes: 69 additions & 20 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,50 @@ jobs:
echo "::notice::Documentation-only change -- skipping the image build."
fi

# Configuration-level lint, in its own job so a bad fragment or patch
# header fails in about a minute BEFORE any kernel leg or the 3h image
# build starts (both `needs` this). Config-only by design: the fragment
# check unpacks the pinned Buildroot tarball (10 MB, hash-verified by the
# wrapper Makefile) and runs kconfig -- no toolchain, no package, no
# compile. See docs/ci.md#lint-config.
lint-config:
name: Lint configuration (patch headers, fragment stacks)
needs: gate
if: needs.gate.outputs.build_needed == 'true'
runs-on: ubuntu-26.04
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# Guards what the build can't: `patch -p1` ignores mail headers, so a
# malformed `From:` builds green and only breaks `git am` at export
# time. See docs/ci.md#patch-lint-placement.
- name: Lint kernel patch headers (git am-ability, both series)
run: scripts/lint-kernel-patches.sh

# Text-level: the image and kernel-only fragment stacks share their
# toolchain/kernel fragments by construction -- assert that structure
# holds (no Buildroot tree needed; the kernel legs re-run this before
# any cache restore). See docs/ci.md#kernel-defconfig-lockstep.
- name: Lint kernel-defconfig lockstep
run: scripts/check-kernel-defconfig-sync.sh

# Resolved-level: regenerate every fragment stack through Buildroot's
# own merge_config.sh + olddefconfig and assert no redefinition
# between fragments, no silently dropped symbol, resolved lockstep,
# and the golden hash of the resolved DE10 configuration. See
# docs/ci.md#lint-config.
- name: Check fragment stacks (regenerate + golden hashes)
run: scripts/check-config-fragments.sh

# KERNEL-ONLY leg per variant (ADR 0021); runs before `build`, which
# consumes its module tree -- a failed leg auto-skips `build` (see
# `status`). Add a variant = one new configs/mister_<name>.fragment;
# everything here derives from the name via `gate`. See docs/ci.md#variants.
build-kernel:
name: Build kernel (${{ matrix.kernel }})
needs: gate
needs: [gate, lint-config]
if: needs.gate.outputs.build_needed == 'true'
runs-on: ubuntu-26.04
strategy:
Expand Down Expand Up @@ -188,7 +225,7 @@ jobs:
# needs on the matrix job aggregates ALL its legs; a failed (or cancelled)
# leg therefore auto-skips this job — `status` below translates that skip
# into "the kernel leg is the root cause", not a second failure.
needs: [gate, build-kernel]
needs: [gate, lint-config, build-kernel]
if: needs.gate.outputs.build_needed == 'true'
runs-on: ubuntu-26.04

Expand All @@ -201,19 +238,10 @@ jobs:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# BEFORE the build (fails in ~1s, not after 300 min). Guards what the
# build can't: `patch -p1` ignores mail headers, so a malformed
# `From:` builds green and only breaks `git am` at export time.
# See docs/ci.md#patch-lint-placement.
- name: Lint kernel patch headers (git am-ability, both series)
run: scripts/lint-kernel-patches.sh

# Kernel-only defconfig is a manually mirrored COPY of the main
# defconfig's toolchain/kernel stanzas -- this asserts it hasn't
# drifted (a main-only edit is easy to forget to mirror).
# See docs/ci.md#kernel-defconfig-lockstep.
- name: Lint kernel-defconfig lockstep
run: scripts/check-kernel-defconfig-sync.sh
# The patch-header and configuration lints that used to sit here run
# in the `lint-config` job above, which this job `needs` -- same
# fail-before-the-3h-build property, minus the wait for the kernel
# legs. See docs/ci.md#lint-config.

# Populates the overlay the build below consumes -- see
# .github/actions/merge-kernel-modules and docs/ci.md#kernel-module-overlay.
Expand Down Expand Up @@ -351,32 +379,53 @@ jobs:
# needs.<job>.result is used instead of success()/failure()).
status:
name: Build status
needs: [gate, build-kernel, build]
needs: [gate, lint-config, build-kernel, build]
if: always()
runs-on: ubuntu-26.04
steps:
- name: Report
env:
GATE_RESULT: ${{ needs.gate.result }}
LINT_RESULT: ${{ needs.lint-config.result }}
KERNEL_RESULT: ${{ needs.build-kernel.result }}
BUILD_RESULT: ${{ needs.build.result }}
BUILD_NEEDED: ${{ needs.gate.outputs.build_needed }}
run: |
set -euo pipefail
echo "gate=$GATE_RESULT build-kernel=$KERNEL_RESULT build=$BUILD_RESULT build_needed=$BUILD_NEEDED"
echo "gate=$GATE_RESULT lint-config=$LINT_RESULT build-kernel=$KERNEL_RESULT build=$BUILD_RESULT build_needed=$BUILD_NEEDED"

if [ "$GATE_RESULT" != "success" ]; then
echo "::error::The gate job did not succeed ($GATE_RESULT), so whether a build was needed is unknown. Failing rather than assuming it wasn't."
exit 1
fi

rc=0
case "$LINT_RESULT" in
success)
echo "lint-config: patch headers, lockstep and fragment stacks are clean."
;;
skipped)
if [ "$BUILD_NEEDED" = "true" ]; then
echo "::error::The gate asked for a build and lint-config was skipped anyway. That is a workflow bug, not a docs change."
rc=1
else
echo "lint-config: documentation-only change, nothing to lint."
fi
;;
*)
echo "::error::lint-config $LINT_RESULT. (Expect build-kernel and build to show as skipped below -- that is this failure cascading through needs, not two more bugs.)"
rc=1
;;
esac

case "$KERNEL_RESULT" in
success)
echo "build-kernel: every kernel leg built and uploaded its module tree."
;;
skipped)
if [ "$BUILD_NEEDED" = "true" ]; then
if [ "$BUILD_NEEDED" = "true" ] && [ "$LINT_RESULT" != "success" ]; then
echo "build-kernel: skipped because lint-config did not succeed -- the needs-cascade, already failed above."
elif [ "$BUILD_NEEDED" = "true" ]; then
echo "::error::The gate asked for a build and build-kernel was skipped anyway. That is a workflow bug, not a docs change."
rc=1
else
Expand All @@ -396,8 +445,8 @@ jobs:
skipped)
if [ "$BUILD_NEEDED" != "true" ]; then
echo "build: documentation-only change, no build needed."
elif [ "$KERNEL_RESULT" != "success" ]; then
echo "build: skipped because build-kernel did not succeed -- the needs-cascade, already failed above."
elif [ "$LINT_RESULT" != "success" ] || [ "$KERNEL_RESULT" != "success" ]; then
echo "build: skipped because lint-config or build-kernel did not succeed -- the needs-cascade, already failed above."
else
echo "::error::The gate asked for a build and build was skipped anyway. That is a workflow bug, not a docs change."
rc=1
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ jobs:
)
sh_relpaths=(
post-build.sh
post-image.sh
initramfs-post-build.sh
initramfs-overlay/init
)
Expand Down Expand Up @@ -249,6 +250,7 @@ jobs:
fat-payload/Scripts/check_storage.sh
fat-payload/Scripts/pair_logitech.sh
post-build.sh
post-image.sh
initramfs-post-build.sh
initramfs-overlay/init
rootfs-overlay/usr/sbin/mister-fsck-exfat
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -654,15 +654,15 @@ jobs:
restore-keys: br-dl-azcopy-

# `echo >> .config` + olddefconfig is the standard Buildroot idiom for
# "the committed defconfig, plus this one symbol". It is used rather than
# a committed fragment because there is exactly one symbol and no make
# target to hang a fragment off (unlike configs/mister_rt.fragment, which
# `make rt` exists to apply). BR2_PACKAGE_HOST_GO and
# "the committed configuration, plus this one symbol". It is used rather
# than a committed fragment because there is exactly one symbol and no
# make target to hang a fragment off (unlike configs/mister_rt.fragment,
# which `make rt` exists to apply). BR2_PACKAGE_HOST_GO and
# BR2_PACKAGE_CA_CERTIFICATES come along via the package's own `select`s.
- name: Configure (defconfig + azcopy)
- name: Configure (DE10-Nano fragment stack + azcopy)
run: |
set -eu
make mister_de10nano_defconfig
make de10nano-defconfig
echo 'BR2_PACKAGE_AZCOPY=y' >> output/.config
make olddefconfig
grep -qx 'BR2_PACKAGE_AZCOPY=y' output/.config
Expand Down
Loading
Loading