From 3f89dea159b97d9862db933443477c5f0d868b01 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Thu, 13 Aug 2026 13:42:27 +0000 Subject: [PATCH 01/19] Initial version of Infix spin example --- .gitignore | 3 + .gitmodules | 3 + Config.in | 1 + Makefile | 38 ++++ README.md | 66 +++++- .../etc/finit.d/available/hello-world.conf | 6 + .../etc/finit.d/enabled/hello-world.conf | 1 + board/common/rootfs/etc/hello-world.conf | 5 + config-snippets/common.conf | 29 +++ config-snippets/x86_64.conf | 3 + configs/x86_64_defconfig | 199 ++++++++++++++++++ external.desc | 2 + external.mk | 1 + infix | 1 + package/Config.in | 3 + package/hello-world/Config.in | 6 + package/hello-world/hello-world.mk | 25 +++ src/hello-world/Makefile | 12 ++ src/hello-world/hello-world.c | 57 +++++ 19 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Config.in create mode 100644 Makefile create mode 100644 board/common/rootfs/etc/finit.d/available/hello-world.conf create mode 120000 board/common/rootfs/etc/finit.d/enabled/hello-world.conf create mode 100644 board/common/rootfs/etc/hello-world.conf create mode 100644 config-snippets/common.conf create mode 100644 config-snippets/x86_64.conf create mode 100644 configs/x86_64_defconfig create mode 100644 external.desc create mode 100644 external.mk create mode 160000 infix create mode 100644 package/Config.in create mode 100644 package/hello-world/Config.in create mode 100644 package/hello-world/hello-world.mk create mode 100644 src/hello-world/Makefile create mode 100644 src/hello-world/hello-world.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0c4c58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +dl/ +output/ +.ccache/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9a5bc82 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "infix"] + path = infix + url = https://github.com/kernelkit/infix.git diff --git a/Config.in b/Config.in new file mode 100644 index 0000000..a4d1924 --- /dev/null +++ b/Config.in @@ -0,0 +1 @@ +source "$BR2_EXTERNAL_EXAMPLE_PATH/package/Config.in" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..63b8ce4 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +# Build the spin by handing over to Buildroot with two br2-externals +# stacked: Infix first, then this repo (branding, overlay, packages). +export BR2_EXTERNAL := $(CURDIR)/infix:$(CURDIR) + +O ?= $(CURDIR)/output +config := $(O)/.config +bmake = $(MAKE) -C infix/buildroot O=$(O) $1 + +all: $(config) infix/buildroot/Makefile + @+$(call bmake,$@) + +$(config): + @+$(call bmake,list-defconfigs) + @echo "ERROR: No configuration selected." + @echo "Please choose a configuration from the list above by running" + @echo "'make _defconfig' before building an image." + @exit 1 + +%_defconfig: configs/%_defconfig + @+$(call bmake,$@) + +# Any other target (menuconfig, clean, ...) is passed to Buildroot. +%: | infix/buildroot/Makefile + @+$(call bmake,$@) + +infix/buildroot/Makefile: + @git submodule update --init --recursive + +# Regenerate configs/ from Infix's defconfigs + our snippets. Run this +# after editing anything in config-snippets/, and commit the result. +defconfigs-generate: infix/buildroot/Makefile + ./infix/utils/generate-defconfig.sh \ + -b infix/configs/x86_64_defconfig \ + -c config-snippets/common.conf \ + -c config-snippets/x86_64.conf \ + -o configs/x86_64_defconfig + +.PHONY: all defconfigs-generate diff --git a/README.md b/README.md index ecf5ea6..85f8256 100644 --- a/README.md +++ b/README.md @@ -1 +1,65 @@ -# infix-spin-example \ No newline at end of file +# Example Infix Spin + +This is a *spin* of [Infix OS](https://github.com/kernelkit/infix) a +tiny skeleton repo showing how to make Infix your own without forking +it. Infix is included as a submodule; this repo only adds a thin layer +on top: + + * **Branding** — OS name, vendor, hostname, and boot tagline, all in + `config-snippets/` + * **Packages** — an example `hello-world` daemon, built from source in + this repo and supervised by finit (`package/`, `src/`) + * **Files** — a rootfs overlay for configuration and service settings + (`board/common/rootfs/`) + +When a new Infix release comes out, you bump the submodule and rebuild. +No fork, no drift, no merge conflicts. Clone it, rename a few things, +and you have your own OS. + +## Get Started + +Clone the repository along with its submodules, and install the tools +Buildroot needs: + +```bash +git clone --recurse-submodules https://github.com/kernelkit/infix-spin-example.git +cd infix-spin-example + +sudo apt install bc binutils build-essential bzip2 cpio \ + diffutils file findutils git gzip \ + libncurses-dev libssl-dev perl patch \ + python3 rsync sed tar unzip wget \ + autopoint bison flex autoconf automake \ + mtools +``` + +Configure and build. The first build takes a while, since it compiles +the cross toolchain and the Linux kernel too: + +```bash +make x86_64_defconfig +make +``` + +Then boot your new OS in QEMU: + +```bash +sudo apt install qemu-system-x86 +make run +``` + +Log in as `admin` with password `admin`, and have a look at the example +daemon, which greets you in the system log once a minute: + +``` +example-os:~$ initctl status hello-world +example-os:~$ sudo grep hello-world /var/log/syslog +``` + +That's it. Have a look around and make it your own. + +## Learn More + + * [Infix documentation](https://kernelkit.org/) + * [Buildroot br2-external manual](https://buildroot.org/downloads/manual/manual.html#outside-br-custom) + * [finit](https://github.com/troglobit/finit) diff --git a/board/common/rootfs/etc/finit.d/available/hello-world.conf b/board/common/rootfs/etc/finit.d/available/hello-world.conf new file mode 100644 index 0000000..3601232 --- /dev/null +++ b/board/common/rootfs/etc/finit.d/available/hello-world.conf @@ -0,0 +1,6 @@ +# Hello World example daemon. Runs in runlevels 2-5; the +# condition delays start until syslogd is up, since the daemon logs to +# syslog. Finit supervises it and restarts it if it crashes. It is +# enabled by the symlink in ../enabled/ -- on a running system you can +# also use `initctl enable hello-world`. +service [2345] /usr/sbin/hello-world -- Hello World example daemon diff --git a/board/common/rootfs/etc/finit.d/enabled/hello-world.conf b/board/common/rootfs/etc/finit.d/enabled/hello-world.conf new file mode 120000 index 0000000..2147c7b --- /dev/null +++ b/board/common/rootfs/etc/finit.d/enabled/hello-world.conf @@ -0,0 +1 @@ +../available/hello-world.conf \ No newline at end of file diff --git a/board/common/rootfs/etc/hello-world.conf b/board/common/rootfs/etc/hello-world.conf new file mode 100644 index 0000000..6f293d1 --- /dev/null +++ b/board/common/rootfs/etc/hello-world.conf @@ -0,0 +1,5 @@ +# Configuration for the hello-world example daemon. +# +# The daemon only checks that this file exists; parsing is left out to +# keep the example small. Put real settings here and read them in +# load_config(), see src/hello-world/hello-world.c in the spin. diff --git a/config-snippets/common.conf b/config-snippets/common.conf new file mode 100644 index 0000000..d21efc4 --- /dev/null +++ b/config-snippets/common.conf @@ -0,0 +1,29 @@ +# Branding -- shows up in /etc/os-release, the boot tagline, the CLI, +# and the generated image file names. Make it yours. +IX_VENDOR="Example Inc." +IX_VENDOR_HOME="https://example.com" +IX_NAME="ExampleOS" +IX_ID="example-os" +IX_TAGLINE="ExampleOS -- an Infix spin" +IX_DESC="Example spin of Infix OS, use as a starting point for your own." +IX_HOME="https://example.com" +IX_DOC="https://example.com/docs" +IX_SUPPORT="mailto:support@example.com" +BR2_TARGET_GENERIC_HOSTNAME="example-os" + +# Keep the RAUC compatible string of vanilla Infix so upgrades can be +# exchanged with stock Infix images of the same architecture. Set your +# own (e.g. "${IX_ID}-${BR2_ARCH}") to lock upgrades to your spin. +IX_COMPATIBLE="infix-${BR2_ARCH}" + +# Version the OS from this repo's git tags: Infix's post-build.sh runs +# `git describe` here for the BUILD_ID in /etc/os-release. +IX_OEM_PATH="${BR2_EXTERNAL_EXAMPLE_PATH}" + +# Keep downloads and compiler cache in the spin repo (survives +# `make clean` / output/ wipes). +BR2_DL_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/dl" +BR2_CCACHE_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/.ccache" + +# Packages from this spin (see package/) +BR2_PACKAGE_HELLO_WORLD=y diff --git a/config-snippets/x86_64.conf b/config-snippets/x86_64.conf new file mode 100644 index 0000000..74c6cab --- /dev/null +++ b/config-snippets/x86_64.conf @@ -0,0 +1,3 @@ +# Stack our rootfs overlay on top of Infix's. Buildroot applies the +# directories in order, so files here override same-named Infix files. +BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/rootfs ${BR2_EXTERNAL_EXAMPLE_PATH}/board/common/rootfs" diff --git a/configs/x86_64_defconfig b/configs/x86_64_defconfig new file mode 100644 index 0000000..7023753 --- /dev/null +++ b/configs/x86_64_defconfig @@ -0,0 +1,199 @@ +BR2_x86_64=y +BR2_x86_corei7=y +BR2_TOOLCHAIN_EXTERNAL=y +BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY=y +BR2_DL_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/dl" +BR2_CCACHE=y +BR2_CCACHE_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/.ccache" +BR2_ENABLE_DEBUG=y +BR2_GLOBAL_PATCH_DIR="${BR2_EXTERNAL_INFIX_PATH}/patches" +BR2_DOWNLOAD_FORCE_CHECK_HASHES=y +BR2_TARGET_GENERIC_HOSTNAME="example-os" +BR2_TARGET_GENERIC_ISSUE="Infix by KernelKit" +BR2_INIT_FINIT=y +BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y +BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs" +BR2_ROOTFS_MERGED_USR=y +# BR2_TARGET_ENABLE_ROOT_LOGIN is not set +BR2_SYSTEM_BIN_SH_BASH=y +BR2_TARGET_GENERIC_GETTY_PORT="@console" +BR2_TARGET_GENERIC_GETTY_TERM="xterm" +BR2_SYSTEM_DHCP="eth0" +BR2_ENABLE_LOCALE_WHITELIST="C en_US en_CA C.UTF-8" +BR2_GENERATE_LOCALE="en_US en_CA C.UTF-8" +BR2_TARGET_TZ_INFO=y +BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/rootfs ${BR2_EXTERNAL_EXAMPLE_PATH}/board/common/rootfs" +BR2_ROOTFS_POST_BUILD_SCRIPT="board/qemu/x86_64/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh" +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_VERSION=y +BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.44" +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/linux_defconfig" +BR2_LINUX_KERNEL_INSTALL_TARGET=y +BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y +BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig" +BR2_PACKAGE_STRACE=y +BR2_PACKAGE_STRESS_NG=y +BR2_PACKAGE_JQ=y +BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y +BR2_PACKAGE_DBUS_CXX=y +BR2_PACKAGE_DBUS_GLIB=y +BR2_PACKAGE_EUDEV_RULES_GEN=y +# BR2_PACKAGE_EUDEV_ENABLE_HWDB is not set +BR2_PACKAGE_GPTFDISK=y +BR2_PACKAGE_GPTFDISK_SGDISK=y +BR2_PACKAGE_PCIUTILS=y +BR2_PACKAGE_RNG_TOOLS=y +BR2_PACKAGE_UBOOT_TOOLS=y +BR2_PACKAGE_UBOOT_TOOLS_FIT_SUPPORT=y +BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y +BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y +BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y +BR2_PACKAGE_LIBSSH_OPENSSL=y +BR2_PACKAGE_LIBSSH2=y +BR2_PACKAGE_LIBSSH2_OPENSSL=y +BR2_PACKAGE_LIBOPENSSL_BIN=y +BR2_PACKAGE_LIBINPUT=y +BR2_PACKAGE_LIBCURL_CURL=y +BR2_PACKAGE_NETOPEER2_CLI=y +BR2_PACKAGE_NSS_MDNS=y +BR2_PACKAGE_SYSREPO_GROUP="sysrepo" +BR2_PACKAGE_LINUX_PAM=y +BR2_PACKAGE_LIBPAM_RADIUS_AUTH=y +BR2_PACKAGE_ONIGURUMA=y +BR2_PACKAGE_AVAHI_DAEMON=y +BR2_PACKAGE_AVAHI_DEFAULT_SERVICES=y +BR2_PACKAGE_CHRONY=y +BR2_PACKAGE_CONNTRACK_TOOLS=y +BR2_PACKAGE_DNSMASQ=y +BR2_PACKAGE_ETHTOOL=y +BR2_PACKAGE_FPING=y +BR2_PACKAGE_FRR=y +# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set +BR2_PACKAGE_IPERF3=y +BR2_PACKAGE_IPROUTE2=y +BR2_PACKAGE_IPTABLES_NFTABLES=y +BR2_PACKAGE_IPUTILS=y +BR2_PACKAGE_LINUXPTP=y +BR2_PACKAGE_LLDPD=y +BR2_PACKAGE_MSTPD=y +BR2_PACKAGE_MTR=y +BR2_PACKAGE_NETCALC=y +BR2_PACKAGE_NETCAT_OPENBSD=y +BR2_PACKAGE_NETSNMP=y +BR2_PACKAGE_NGINX=y +BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y +BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y +BR2_PACKAGE_NMAP=y +BR2_PACKAGE_NMAP_NCAT=y +BR2_PACKAGE_NMAP_NMAP=y +BR2_PACKAGE_NMAP_NPING=y +BR2_PACKAGE_ODHCP6C=y +BR2_PACKAGE_OPENRESOLV=y +BR2_PACKAGE_OPENSSH=y +BR2_PACKAGE_SOCAT=y +BR2_PACKAGE_TCPDUMP=y +BR2_PACKAGE_TRACEROUTE=y +BR2_PACKAGE_ULOGD=y +BR2_PACKAGE_WHOIS=y +BR2_PACKAGE_WIREGUARD_TOOLS=y +BR2_PACKAGE_BASH_COMPLETION=y +BR2_PACKAGE_NEOFETCH=y +BR2_PACKAGE_SUDO=y +BR2_PACKAGE_TTYD=y +BR2_PACKAGE_GETENT=y +BR2_PACKAGE_HTOP=y +BR2_PACKAGE_IRQBALANCE=y +BR2_PACKAGE_KMOD_TOOLS=y +BR2_PACKAGE_PWGEN=y +BR2_PACKAGE_RAUC=y +BR2_PACKAGE_RAUC_DBUS=y +BR2_PACKAGE_RAUC_GPT=y +BR2_PACKAGE_RAUC_NETWORK=y +BR2_PACKAGE_RAUC_JSON=y +BR2_PACKAGE_SYSKLOGD=y +BR2_PACKAGE_SYSKLOGD_LOGGER=y +BR2_PACKAGE_WATCHDOGD=y +BR2_PACKAGE_WATCHDOGD_GENERIC=y +BR2_PACKAGE_WATCHDOGD_LOADAVG=y +BR2_PACKAGE_WATCHDOGD_FILENR=y +BR2_PACKAGE_WATCHDOGD_MEMINFO=y +BR2_PACKAGE_WATCHDOGD_FSMON=y +BR2_PACKAGE_WATCHDOGD_TEMPMON=y +BR2_PACKAGE_LESS=y +BR2_PACKAGE_MG=y +BR2_PACKAGE_NANO=y +# BR2_TARGET_ROOTFS_TAR is not set +BR2_TARGET_EDK2=y +BR2_TARGET_GRUB2=y +BR2_TARGET_GRUB2_X86_64_EFI=y +BR2_TARGET_GRUB2_BUILTIN_MODULES_EFI="boot linux ext2 squash4 part_gpt normal efi_gop configfile loadenv test echo reboot net efinet tftp loopback cat search" +BR2_TARGET_GRUB2_BUILTIN_CONFIG_EFI="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/grub-embed.cfg" +BR2_TARGET_GRUB2_INSTALL_TOOLS=y +BR2_PACKAGE_HOST_DOSFSTOOLS=y +BR2_PACKAGE_HOST_E2FSPROGS=y +BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y +BR2_PACKAGE_HOST_GENEXT2FS=y +BR2_PACKAGE_HOST_GO_BIN=y +BR2_PACKAGE_HOST_MTOOLS=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y +IX_VENDOR="Example Inc." +IX_VENDOR_HOME="https://example.com" +IX_NAME="ExampleOS" +IX_ID="example-os" +IX_COMPATIBLE="infix-${BR2_ARCH}" +IX_TAGLINE="ExampleOS -- an Infix spin" +IX_DESC="Example spin of Infix OS, use as a starting point for your own." +IX_HOME="https://example.com" +IX_DOC="https://example.com/docs" +IX_SUPPORT="mailto:support@example.com" +IX_OEM_PATH="${BR2_EXTERNAL_EXAMPLE_PATH}" +BR2_PACKAGE_FEATURE_GPS=y +BR2_PACKAGE_FEATURE_WIFI=y +BR2_PACKAGE_FEATURE_WIFI_MEDIATEK=y +BR2_PACKAGE_FEATURE_WIFI_REALTEK=y +BR2_PACKAGE_FEATURE_WIFI_QUALCOMM=y +BR2_PACKAGE_CONFD=y +BR2_PACKAGE_NETD=y +BR2_PACKAGE_CONFD_TEST_MODE=y +BR2_PACKAGE_CURIOS_HTTPD=y +BR2_PACKAGE_CURIOS_NFTABLES=y +BR2_PACKAGE_GENCERT=y +BR2_PACKAGE_STATD=y +BR2_PACKAGE_FACTORY=y +BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y +BR2_PACKAGE_FINIT_PLUGIN_HOOK_SCRIPTS=y +BR2_PACKAGE_FINIT_PLUGIN_MODULES_LOAD=y +BR2_PACKAGE_FINIT_PLUGIN_RTC=y +BR2_PACKAGE_FINIT_RTC_DATE="2024-11-04 10:54:00" +BR2_PACKAGE_FINIT_RTC_FILE="/var/lib/misc/rtc" +BR2_PACKAGE_FINIT_PLUGIN_TTY=y +BR2_PACKAGE_FINIT_PLUGIN_URANDOM=y +BR2_PACKAGE_FIREWALL=y +BR2_PACKAGE_IITO=y +BR2_PACKAGE_KEYACK=y +BR2_PACKAGE_KLISH_PLUGIN_INFIX=y +BR2_PACKAGE_LOWDOWN=y +BR2_PACKAGE_MCD=y +BR2_PACKAGE_MDNS_ALIAS=y +BR2_PACKAGE_NETBROWSE=y +BR2_PACKAGE_ONIEPROM=y +BR2_PACKAGE_PODMAN=y +BR2_PACKAGE_PODMAN_DRIVER_BTRFS=y +BR2_PACKAGE_PODMAN_DRIVER_DEVICEMAPPER=y +BR2_PACKAGE_PODMAN_DRIVER_VFS=y +BR2_PACKAGE_TETRIS=y +BR2_PACKAGE_ROUSETTE=y +BR2_PACKAGE_WEBUI=y +BR2_PACKAGE_RAUC_INSTALLATION_STATUS=y +BR2_PACKAGE_HOST_PYTHON_YANGDOC=y +IX_IMAGE_ITB_AUX=y +IX_IMAGE_ITB_QCOW=y +IX_IMAGE_ITB_RAUC=y +IX_IMAGE_README=y +IX_TRUSTED_KEYS=y +IX_TRUSTED_KEYS_DEVELOPMENT=y +BR2_PACKAGE_HELLO_WORLD=y diff --git a/external.desc b/external.desc new file mode 100644 index 0000000..d2b7fcb --- /dev/null +++ b/external.desc @@ -0,0 +1,2 @@ +name: EXAMPLE +desc: Example Infix OS Spin diff --git a/external.mk b/external.mk new file mode 100644 index 0000000..401ab65 --- /dev/null +++ b/external.mk @@ -0,0 +1 @@ +include $(sort $(wildcard $(BR2_EXTERNAL_EXAMPLE_PATH)/package/*/*.mk)) diff --git a/infix b/infix new file mode 160000 index 0000000..279fc86 --- /dev/null +++ b/infix @@ -0,0 +1 @@ +Subproject commit 279fc868a11228d5b932d54c74744ca0b11a06fc diff --git a/package/Config.in b/package/Config.in new file mode 100644 index 0000000..631bf53 --- /dev/null +++ b/package/Config.in @@ -0,0 +1,3 @@ +menu "ExampleOS packages" + source "$BR2_EXTERNAL_EXAMPLE_PATH/package/hello-world/Config.in" +endmenu diff --git a/package/hello-world/Config.in b/package/hello-world/Config.in new file mode 100644 index 0000000..a50e387 --- /dev/null +++ b/package/hello-world/Config.in @@ -0,0 +1,6 @@ +config BR2_PACKAGE_HELLO_WORLD + bool "hello-world" + help + Example daemon for the spin: logs a greeting to syslog + once a minute. Started by finit, see + board/common/rootfs/etc/finit.d/available/hello-world.conf diff --git a/package/hello-world/hello-world.mk b/package/hello-world/hello-world.mk new file mode 100644 index 0000000..8dc898d --- /dev/null +++ b/package/hello-world/hello-world.mk @@ -0,0 +1,25 @@ +################################################################################ +# +# hello-world +# +################################################################################ + +HELLO_WORLD_VERSION = 1.0 +# Build from source vendored in this repo instead of a download URL. +HELLO_WORLD_SITE_METHOD = local +HELLO_WORLD_SITE = $(BR2_EXTERNAL_EXAMPLE_PATH)/src/hello-world +HELLO_WORLD_LICENSE = MIT + +define HELLO_WORLD_BUILD_CMDS + $(MAKE) -C $(@D) \ + CC="$(TARGET_CC)" \ + CFLAGS="$(TARGET_CFLAGS) -Wall" \ + LDFLAGS="$(TARGET_LDFLAGS)" +endef + +define HELLO_WORLD_INSTALL_TARGET_CMDS + $(INSTALL) -D -m 0755 $(@D)/hello-world \ + $(TARGET_DIR)/usr/sbin/hello-world +endef + +$(eval $(generic-package)) diff --git a/src/hello-world/Makefile b/src/hello-world/Makefile new file mode 100644 index 0000000..a32ac3c --- /dev/null +++ b/src/hello-world/Makefile @@ -0,0 +1,12 @@ +CC ?= cc +CFLAGS ?= -O2 -Wall + +all: hello-world + +hello-world: hello-world.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +clean: + rm -f hello-world + +.PHONY: all clean diff --git a/src/hello-world/hello-world.c b/src/hello-world/hello-world.c new file mode 100644 index 0000000..f248c81 --- /dev/null +++ b/src/hello-world/hello-world.c @@ -0,0 +1,57 @@ +/* hello-world - example daemon for the Infix spin + * + * Periodically writes a greeting to the system log. Runs in the + * foreground, as finit expects, which handles supervision and restart. + */ +#include +#include +#include +#include + +#define CONFIG_FILE "/etc/hello-world.conf" +#define INTERVAL 60 + +static volatile sig_atomic_t running = 1; + +static void sig_exit(int signo) +{ + running = 0; +} + +/* + * The configuration file is shipped by the rootfs overlay, see + * board/common/rootfs/etc/ in the spin. A real daemon would read its + * settings here; this example only verifies the file is in place. + */ +static int load_config(void) +{ + FILE *fp; + + fp = fopen(CONFIG_FILE, "r"); + if (!fp) { + syslog(LOG_WARNING, "missing %s, using defaults", CONFIG_FILE); + return -1; + } + + fclose(fp); + return 0; +} + +int main(void) +{ + signal(SIGTERM, sig_exit); + signal(SIGINT, sig_exit); + + openlog("hello-world", LOG_PID, LOG_DAEMON); + load_config(); + + while (running) { + syslog(LOG_INFO, "Hello World!"); + sleep(INTERVAL); + } + + syslog(LOG_INFO, "exiting"); + closelog(); + + return 0; +} From 0f00c8e9c9c029dca738b8136a10ceebaf5cb7eb Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Mon, 17 Aug 2026 08:48:59 +0200 Subject: [PATCH 02/19] Add CI workflow and hello-world regression test Build and test the spin on a stock GitHub runner: x86_64 image built with Buildroot, then booted in QEMU by Infix's test harness. The suite reuses the harness and only overrides TESTS, so the spin carries just its own cases. One case for now, checking that finit supervises the hello-world daemon, that its config file comes from the rootfs overlay, and that it greets us in the system log. Signed-off-by: Richard Alpe --- .github/workflows/build.yml | 59 ++++++++++++++++++++++++++++++ .gitignore | 1 + Makefile | 13 ++++++- README.md | 21 +++++++++++ test/9pm-proj.yaml | 6 +++ test/case/all.yaml | 11 ++++++ test/case/hello-world/test.py | 28 ++++++++++++++ test/case/hello-world/topology.dot | 27 ++++++++++++++ 8 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build.yml create mode 100644 test/9pm-proj.yaml create mode 100644 test/case/all.yaml create mode 100755 test/case/hello-world/test.py create mode 100644 test/case/hello-world/topology.dot diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..5e71e75 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,59 @@ +name: Build & Test + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + x86_64: + name: ExampleOS x86_64 + runs-on: ubuntu-latest + steps: + - name: Free Disk Space + uses: jlumbroso/free-disk-space@main + with: + tool-cache: true + android: true + dotnet: true + haskell: true + large-packages: true + docker-images: true + swap-storage: false + + - name: Install Build Dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + bc binutils build-essential bzip2 cpio \ + diffutils file findutils git gzip \ + libncurses-dev libssl-dev perl patch \ + python3 rsync sed tar unzip wget \ + autopoint bison flex autoconf automake \ + mtools + + # Full history, the OS version comes from `git describe` here. + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + submodules: recursive + + - name: Configure x86_64 + run: make x86_64_defconfig + + - name: Build x86_64 + run: make + + - name: Test x86_64 + run: make test + + - name: Publish Test Result + if: always() + run: cat infix/test/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY + + - name: Upload Images + uses: actions/upload-artifact@v7 + with: + name: example-os-x86_64 + path: output/images/ diff --git a/.gitignore b/.gitignore index a0c4c58..90542ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dl/ output/ .ccache/ +__pycache__/ diff --git a/Makefile b/Makefile index 63b8ce4..79bd228 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,10 @@ # stacked: Infix first, then this repo (branding, overlay, packages). export BR2_EXTERNAL := $(CURDIR)/infix:$(CURDIR) +# Run our tests instead of Infix's, using Infix's test harness, see test/ +export TESTS := $(CURDIR)/test/case/all.yaml +export NINEPM_PROJ_CONFIG := $(CURDIR)/test/9pm-proj.yaml + O ?= $(CURDIR)/output config := $(O)/.config bmake = $(MAKE) -C infix/buildroot O=$(O) $1 @@ -19,6 +23,13 @@ $(config): %_defconfig: configs/%_defconfig @+$(call bmake,$@) +# Boot the image in QEMU and run $(TESTS) against it. Needs Docker or +# Podman; the harness pulls a container with QEMU and the test tooling. +# Explicit rule, since the catch-all below would find the test/ directory +# and think there is nothing to do. +test: | infix/buildroot/Makefile + @+$(call bmake,$@) + # Any other target (menuconfig, clean, ...) is passed to Buildroot. %: | infix/buildroot/Makefile @+$(call bmake,$@) @@ -35,4 +46,4 @@ defconfigs-generate: infix/buildroot/Makefile -c config-snippets/x86_64.conf \ -o configs/x86_64_defconfig -.PHONY: all defconfigs-generate +.PHONY: all test defconfigs-generate diff --git a/README.md b/README.md index 85f8256..50db99b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ on top: this repo and supervised by finit (`package/`, `src/`) * **Files** — a rootfs overlay for configuration and service settings (`board/common/rootfs/`) + * **Tests** — a regression test for the daemon, run in QEMU by Infix's + test harness (`test/`) When a new Infix release comes out, you bump the submodule and rebuild. No fork, no drift, no merge conflicts. Clone it, rename a few things, @@ -58,6 +60,25 @@ example-os:~$ sudo grep hello-world /var/log/syslog That's it. Have a look around and make it your own. +## Test + +Infix comes with a test harness that boots the image in QEMU and pokes +at it over SSH and NETCONF. A spin can reuse it for its own tests; this +one has a single case that checks the `hello-world` daemon, see +`test/case/hello-world/`: + +```bash +make test +``` + +Docker or Podman is required, the harness runs inside a container that +brings its own QEMU and Python tooling. Tests describe the *logical* +topology they need (`topology.dot`) and the harness maps it onto the +virtual network it starts, so nothing here depends on real hardware. + +The same two commands run in CI, on a stock GitHub runner, see +`.github/workflows/build.yml`. + ## Learn More * [Infix documentation](https://kernelkit.org/) diff --git a/test/9pm-proj.yaml b/test/9pm-proj.yaml new file mode 100644 index 0000000..6422b3a --- /dev/null +++ b/test/9pm-proj.yaml @@ -0,0 +1,6 @@ +# Tell 9pm which project it is testing. Paths are relative to the 9pm +# submodule, infix/test/9pm/, so "../../../" is the root of this spin. +9pm: + PROJECT-NAME: "ExampleOS" + PROJECT-ROOT: "../../../" + PROJECT-TOPDOC: "../case/" diff --git a/test/case/all.yaml b/test/case/all.yaml new file mode 100644 index 0000000..40b79a6 --- /dev/null +++ b/test/case/all.yaml @@ -0,0 +1,11 @@ +# Test suite for this spin. `make test` runs it instead of Infix's own +# suite, see TESTS in the top-level Makefile. Add a line per test case; +# to also run (parts of) the Infix suite, point at it with a relative +# path, e.g.: +# +# - name: "System" +# suite: ../../infix/test/case/system/all.yaml +# +--- +- name: "Hello World Daemon" + case: hello-world/test.py diff --git a/test/case/hello-world/test.py b/test/case/hello-world/test.py new file mode 100755 index 0000000..2064551 --- /dev/null +++ b/test/case/hello-world/test.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +"""Hello World daemon + +Verify the pieces this spin adds on top of Infix: the hello-world +package is installed, finit supervises it, its configuration file comes +from the rootfs overlay, and it greets us in the system log. + +""" +import infamy + +with infamy.Test() as test: + with test.step("Set up topology and attach to target DUT"): + env = infamy.Env() + tgtssh = env.attach("target", "mgmt", "ssh") + + with test.step("Verify configuration file from the rootfs overlay"): + tgtssh.runsh("test -f /etc/hello-world.conf", check=True) + + with test.step("Verify finit supervises the hello-world service"): + status = tgtssh.runsh("initctl status hello-world", check=True).stdout + print(status) + if "running" not in status: + test.fail() + + with test.step("Verify the greeting reached the system log"): + tgtssh.runsh("sudo grep -q 'Hello World!' /var/log/syslog", check=True) + + test.succeed() diff --git a/test/case/hello-world/topology.dot b/test/case/hello-world/topology.dot new file mode 100644 index 0000000..9f8d895 --- /dev/null +++ b/test/case/hello-world/topology.dot @@ -0,0 +1,27 @@ +// Logical topology required by this test: one DUT reachable over a +// management link. Infamy maps it onto whatever physical topology the +// test environment provides, so the same test runs in QEMU or on real +// hardware without changes. +graph "1x1" { + layout="neato"; + overlap="false"; + esep="+80"; + + node [shape=record, fontname="DejaVu Sans Mono, Book"]; + edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; + + host [ + label="host | { mgmt }", + pos="0,12!", + requires="controller", + ]; + + target [ + label="{ mgmt } | target", + pos="10,12!", + + requires="infix", + ]; + + host:mgmt -- target:mgmt [requires="mgmt", color="lightgray"] +} From 44f0cb90c170fccb6f9cd741604ea228cacf1f58 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Mon, 17 Aug 2026 13:24:35 +0200 Subject: [PATCH 03/19] Generate a branded PDF test report Infix renders the 9pm result as a PDF with report.py; wire it up for the spin too and point LOGO at our own logo, so the report comes out branded as ExampleOS rather than Infix. CI installs asciidoctor-pdf and uploads the report along with the images. --- .github/workflows/build.yml | 9 +++++++++ Makefile | 11 ++++++++++- README.md | 14 ++++++++++++-- doc/logo.png | Bin 0 -> 62970 bytes 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 doc/logo.png diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e71e75..2126dc3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,9 @@ jobs: autopoint bison flex autoconf automake \ mtools + - name: Install asciidoctor-pdf + run: sudo gem install --no-document asciidoctor-pdf + # Full history, the OS version comes from `git describe` here. - name: Checkout uses: actions/checkout@v6 @@ -52,7 +55,13 @@ jobs: if: always() run: cat infix/test/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY + # Lands in output/images/, uploaded with the rest below. + - name: Test Report x86_64 + if: always() + run: make test-report + - name: Upload Images + if: always() uses: actions/upload-artifact@v7 with: name: example-os-x86_64 diff --git a/Makefile b/Makefile index 79bd228..4bf4cfd 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ export BR2_EXTERNAL := $(CURDIR)/infix:$(CURDIR) export TESTS := $(CURDIR)/test/case/all.yaml export NINEPM_PROJ_CONFIG := $(CURDIR)/test/9pm-proj.yaml +# Brand the generated test report with our logo instead of the Infix one. +# The bracket suffix is an Asciidoctor image spec, tweak to taste. +export LOGO := $(CURDIR)/doc/logo.png[top=40%, align=right, pdfwidth=10cm] + O ?= $(CURDIR)/output config := $(O)/.config bmake = $(MAKE) -C infix/buildroot O=$(O) $1 @@ -30,6 +34,11 @@ $(config): test: | infix/buildroot/Makefile @+$(call bmake,$@) +# Render the result of the last `make test` as output/images/test-report.pdf. +# Needs asciidoctor-pdf: gem install asciidoctor-pdf +test-report: | infix/buildroot/Makefile + @+$(call bmake,$@) + # Any other target (menuconfig, clean, ...) is passed to Buildroot. %: | infix/buildroot/Makefile @+$(call bmake,$@) @@ -46,4 +55,4 @@ defconfigs-generate: infix/buildroot/Makefile -c config-snippets/x86_64.conf \ -o configs/x86_64_defconfig -.PHONY: all test defconfigs-generate +.PHONY: all test test-report defconfigs-generate diff --git a/README.md b/README.md index 50db99b..0313754 100644 --- a/README.md +++ b/README.md @@ -76,8 +76,18 @@ brings its own QEMU and Python tooling. Tests describe the *logical* topology they need (`topology.dot`) and the harness maps it onto the virtual network it starts, so nothing here depends on real hardware. -The same two commands run in CI, on a stock GitHub runner, see -`.github/workflows/build.yml`. +The result of the last run can be rendered as a PDF, branded with the +logo in `doc/` (swap it for your own, and see `LOGO` in the Makefile if +you want it placed differently): + +```bash +gem install asciidoctor-pdf +make test-report # -> output/images/test-report.pdf +``` + +The same commands run in CI, on a stock GitHub runner, see +`.github/workflows/build.yml`. The report is uploaded along with the +images. ## Learn More diff --git a/doc/logo.png b/doc/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bbdb65fb95b0d96abdbb786d1cca4dc7dd35b3ba GIT binary patch literal 62970 zcmdSB1yh_`6D>TrI|SF@1Sddn4IbQGf?IHR3k27}J;B}G-Q6L$4({?z&bc}Fy+7fr zq9}@Dp1pT>@71eU_rrIEFA^w-1c(3t07XhtObGyh0sjmk01pfPP2U6KIRJ3eB_$@T z;+B50=I%vg<}G+Wap#`7G-t@`g2{`3ASFq;l!QrBpWf>dRfoW3LpSgXjw?Z^2{9p# z8$p*59ey&&;aWY)p>{IK(qrc z3w152jXNYm?N}F6S5q@Hfh+HJG_oLc5qR?d|9|^Nn|g-x%Mp6-P!EorUIHLgeTDe< zSii%h6b5At)$`Fq7TfBQ0oh?U0%+L^WP)Q>G`&H)SEY!6Zd3*L4ZZ1((7epaKuR&} zzc%U3+0#=tIhUxVwKNKo1ktz^f$ay?H7S*Tm|?_P{h(+IcJGgVRBiZwJqsH9O3aWWPBmSkA9 zbo0!V=fLr$U}<2@A}{Pcw7EB*XK2%<(sy8#aDLZ1F{J^`q2FR(hmvql;CPY`ltW7` zTK(EJFyfZzHLdBHx+dQjO+qm_G$`c1-KVpA&M=z}JKr7k^mJWaab+S`@B&Z8&wX%e z?9_R<)nGJZrUC4WynX-|)RR*u0VHzjAW45&0E>_`L_UPeIcJD#fw!0kZ&*F%R3w>q zTK{B}kzvolc;<*5n?*qmatH3;rS`%!aj`=TqhbAMj~sSEx|J6pE}Yl&EFDR}iFsfM zwGzPzQ7far_vq-3VrEkiZ3bch0(9ImJ!>91fO_vTb0m-b65T(nRxKZj)&>A@F`2|L zhr{y8uY0Z7=AB=A=Nv2Jo7@dge?(El_Mdl5n>4ZhEJ`9MC3`$7L-e^`HXgsSn<%V|j)!3o;;eeO?Y zo6)B&(=V>|C&UlBV?u$;VTsQwL0A61g@5vW@IdyxBz~jYfdEVUd4kW5=gV#<%gNMs z=snVZ;bfZTzlE>72&HQ>$tbztA1rKxE?XQ08ZHc9;0hO2`N}M1}bVOFFewdJa z?ADr%pKi3iGjq&qu@g1)PR6ywe*b}vY0@Ja;+1=%1Q#OAr4@6>>#np}dwz|ajf*{L z$`VE_bdZQFtLZHOonu^mJn6Q|k#xgrPFx>0-!@{3>QQQmUxbW;R zVyKh|oZDYTMqNfvBrR+R_PUZnJbu%-cHHgz-Zj&h&{qHV;j3Aj$#=JvQO$$8ZVw-6 z+&+Rb8uMdhwIvJBU zOmdkVtT^?2+ZV2$RARi{`5G4jvMDLCbH~v!V%aVx1!RKMh&iq^eZ?=<6lZt4Gf7bP zn%l_#K1L*r^~ME4-f%H5_t!|-O5>p}HpjDICHs0(K5h;=JAmDjf?|dx>*Tzv!shk* z@eTmEbPpEklD|4a`1^?8pJwI3NcUi?kR$Z^JQ6{gOLhfgC(*g?Sww+t1Z!wYeJQ-H zxSR~zu^USIHn}(P_UaFgiX0)#cmD;2bFn~xh$&(V1&+9`?dfit{safJm%7C!ox~`j z*iBWGNLVFz?MWjpK$d3p{sVBlrg1yzEV4G>x z6A7a?1ldg1b|&<6N7PiTs|#^ae)#YHAC1)BzA}UcSuw3#=g3}lyY>Zo8Y3VCr|3%3 zVWFKy%k{K6rf1$oiuijiZV3C0{?B^x7A*>Bo*ELmJM%WJXSzHq&(7Ke_0aP(O-L2u zd=I@{`LmU>ChM6{a72KCxrprD-wSipI}7Ld&?=IXHnHNihMC89Cp2YbXH1=IK@7_n zl!7@*W)=XeOu%zry&`ZIX9dnPU_@8>zuuX#9`0!ZCnJDS*|n=`q2c$2s^l1juup+u zZI=S3@k3sBt&xJfOyMHYUV2dwe^>40NSlth((#G^-aJ^=cHL|{V?Qf%dM+#@=0U$8 z49_|ime5e84h-3@o!)>M=0gAL6?oQ+!js>ve69AU{m0Dnz3ozMxzHVwBimRAZmqsvDj8Wo%}l7cVoSrAFX zTf5gU-?20PPwe?LtIPfO*@!~QUr6tNiTJmY;5W{4j0&#x^{9!6kneF*7=eVAq2E2) zUAe2hyu>04|7X)esj;4B@S|qMZPyYP7*(mCrrZwv-Yc=~Xuv~IOuM*s!k}C+KQ9dy za)FJB@kvxcE@q+QsEk-IsM;Vr&D~%jsb%4>L2X%y9O=C3MW~LkoRJc)2b7g##;zP zR!1;XQ=s;c9RAPo{EPMdl0y--HqJlKaNCi1?h8@k2#bWh&%+J`P`r^Clx`IL^$@vb z$&v7uN9o<*KF(l=zb-QPnxQ9>neNJnk`*uy?{X3A)5geJQv7E+|0CGohgN#*L46j7 zZxKqs#BvVGo+$8V$SFV>GfVDQ+jtd#e)`0}XMk7aRYgH`)TjV-GTafYO(qzcs*ere zii~228QdD|p-VFS&z~x*5|1ZO)Et4L4|3*>Z8?E^SJoDTQsO!2xkjV1(pY{kIs}^S z4_C~dc%#r1hwA@%bXM|)9uZiiK=zh4pBGJ&!mi7QY?QPl4c-?6n&3DGQ#P3+2_Q%} zmPr2ZNx}$Pb1(Ar^mLq!wYBwf1$NDyYKJ6cft&xG3jJ8a zN`sw%!YSCsVNLn;!iMdHDgcc>;JX6CckW!HlsLg6VMABL(Ea^A3=pf7-iZJ1JxYhk zEW9tbU^G{d-v5_07xf4J@_M4$o4dsjvM z%synC&HWpISUo3%I6!gPx?_a`Y=8cD^E+L^+zvrrncY#S%WclAjE5Q%Q)(=TJ2M!` z$k^!o8+q7>dA=w;B)Uu~8h^_Pp>)1NFOt~}h-Gl~k>qr;Wqv&Gq)jDgFSdfnZQ|(< z>@Js#w5^HaUv2-mBf{lYXx&f@9h`>+tUUFb9ulKgQlJe=`2YsMZ0sZ`DdySVP8BoP zWI={=9>e1v7yvOgFbi1HP$$E`b22I~lLsi&2?enMkr z6$Jc_NeS~inx1YIIG+ZJ^D~I6{0XfmyZqf)@ZeK_=TO9Y_q%9HB}p2}NB^bBYUE$pS(yXyq$#q14`%HrQSiWy= z#g(U!a%;5?4uDhb8SJFVp%asSV{$Q7N_Nr#fXd3|V7vrQITIg*X{sox544~Br{mIK z6wVfFi|3D#S>GFBdK2Rka;n(5fE8*HMtuyF>pGC6D zA+@l0o0QK`o^X|LDP8NVH84V>1_}<#@?;b-hzYrX{QcNzX0ig|d_4%jdeWwGi!Yc4 zxT2f0OX`BS+dn>z%8)<-u>C4SouAa~bA)C{xaEFU{**`##Y}FPgCVrj*n&z7bR;wy zFpH0tFR;dfDMm05wAFst*^q2VbVO#s%UdE5I*g2-IxX0vn;wW&k`YGC$L{pIL@nol z*8e^5$1%xdbtpTafV>tClr`V&pZX7j_mjAiko$+F@5)@3?kZP%F+@}d18p`{ESeXL zkAe}y((ySNRFlfhnDe5u(C7lpo)J3_I}gm7{!bZZ6p-TZpr`vGCbBt>_YT*(HD`_R zS%B}2$w;q^Fcmol+Wo|6aXMRO7#ac{gOvuS!Wc_WK}$!5BQ+=Aw_4sOo|vz5D2RH< z7(+6a6R8$eEgn^^rhzo559_$RG3Q|D%~fsU>D#P$G!&ShcIDlDw-mj5F zhP!#&Ri}_?YUrnpt8+8L8R&TuYnk&fLkU>a6*Y^SEAkaYj^>BY{NA3dMy3>BxE2JvJJ9kD(#?x z{7kcua^r89iPPML5ewz#oQ$gvRj`5L!aM`~e5#8a6^em&+8_z3CB41eQ$Kg`Mx0Z1 z`tdRPr=6WCZjZ;YA_0ux65wJzH?FZtIfnGa@5esAl*Wq=*?pcugt|0Au`F8+r2!FF4%|uEm_*Q@`4F$k>NA;bv-$M%99v^yF zWbdwhWowxwOV0F_wBwMdDELdCLMdTNxMxWk&I}w>Kel7}adq5#4Mpm>4$Luo z1C&{eiY;-KO?%lOs!W1J!Yqw%{eHVOFNprj;aJmHgZZT-7fEhS#yfuZ-+c`XVWeKU zUdX>u;`N$bnl@14moy2JG;9`);rK;KF*feNSG!RitqB}updEa@e`c_I9!iZ|*awq3 z|FE7+l9y*R9?_jEM1%0==GD{@-+IfnM6PEQVE-Fg*+k9zz;)DxrFxH4Ck?h(@&H&* zmjZH9u=BwB4c3$qB7t5BJBq%YxlX0?*lyZ7nqLn|`5|YiW!&(`)NW~b^2L!Y@qf3i zNGOFv-t-H}cJ1pz8?x~243GK2^`X5gWRWNX+_Azm+5|udGmaH#eKjIEHNtd%FSkJ- z+57OzszQpm91Ma*Jm*u>$h*-|qEnteL3 z;a68;$QM|@HEmBT4;wyWs^q^_QV_Z``JXDj8`g6#>(zcLz!TCV z#4~XacUF2bN4pCKUIK^4J!^=UCo-3?is*MKN0Fri1<^6EW2w#Mr0MuPkhRp1! zn2O(l`~Xp*cR>#TMo9Af0jEC4SXi-C;j9r~3`>1f7i zE3th?WMHuTjk7B%|ICjbFmHQ3ozS1m=U}eC7({Zmo&e-v1vvsN(FpblU08MapiQ|c zetk!6qAI}l94P153G9bauUKoe@R=1?r%v=dCRU(fr`W%urw@AlBUwZ_?Fqu0yNovv z1qF@TL#xrxWG9T%;-Yo&J~7<8A+gr`Suo(gkqz9&y88^ZTkU6j41#`}qBtWi_htwyefuNG~KdPY0h7SJkvZt(FmgYh> zU2(piaNAr@Bn-LqiavdaO?8vhK!ukxkexI?8SL}}L;A@1TM?x4s!rqi!o{(-(Z!x2;wa!T}%KcFTJAd6p*dq zaA?c2Gm7e^Z70py8jJQ8nD4rCQ0b9+qEN$y^tK-D(qy-hwsT%wa(g0yOISjMhM8YV zUe0djU;R>ih@gM2s(xrSzyIWu5i@}`8Og`eJSNoZ-ANW#F#uRa*-X7;E_*nCBR);N zW)oy<4C(v?JFW`s-%dnLXrQ`xOTo9l!6uLfml1zOx+R7zby{?N`=!VTjqQPD6W-rj zP*|wx06$>H^yf6$_^S6%Xb*1pgar3zk?OlXpU!^LCWGbxm48|cr`q_zwy^LP;^epr zlD_o-05nSssIaml(4~R7kc!xo`{>A_A={qR?+NgHv&;~F+{odCko`txZf#pB3YP+r zxfo+YK~W(S=xH^?|To*Fv-2MvEhjy5~$S)QVpEJP;?B|u22Zxc^Qp;6Z|KqSZBIe;h%htTIUN892mD#|Wq1@2Zz_A(-r z7K*wSZ*a}J(K9uND*&@e1MrChr;w@ztCzveW56qaybTXZ{-#Xe`TELXF@08<8F_62C( z=j3O{hj*OMlo^}7{BIz2^~EV+j9Aj9lV%3?F{F23iI<_A+T^xWMJONJ+M<%T@uImT zyf0vEU-RZp0$W_SU1QN8qRs;oAk>S&a{CyGggS$M`x(IHvIlCUunN@|IoW`mWB|jF zoJC>|RBR9Sp6mYujdbe0Wo;*B$*^)r>ugOqCF`lyH5Z&d(@{TKK}i!j*DqCHOI`aQ zKvERem-cySw*gP*FF}1ij z&x)h%fV+(ooInZ29Zhkt_N@19jSl+Ch081EyNY;X+~`T-I4wGgo4UvLo_xoBM|@1V zC@!KhsaF&a2X{WpiIiy1Tp-{TuJf(5<#+Rpjm5c~flq`*iYviNoN1-md4e?*!oPaS zpPSBF5@Z-1g-CeP94WYxE!$&ua_*OLkWV_g4jYN|BQo?Uf%tV>yWHn6>JLBFZn&(y zy6rWoz4_jX%eGn9;z|xDg_<;4g%2ATmf%{&jxrXJ?)Zke2Wt7%@!kP2 zqcRqhE?PkkNm9zh8hMo(=a(GC*I{H@fE;H2%LMK9>@>^0uW($Z{oOUjl4$W$A*qC- zdb5D6As!tdti1Ui7l6p{`b^{dG3()`^gwq>b_T}fz~D-yG~s{In*Y*$E~>1Ry5qro z@jeRx*{Yg|HUN{d1S=CSu|J?vQmSe`?_Hp_IlC?yL#*{ z3&>Sg(_#i0unyb~I#+DpRv9>*eNLmYL2D)31Z8%K=n{&Lk9$}2p*O`Ei!6Z6EWq;& z%tP8n=Lt5TbihM@WNX@)^#Y-Dc?Mc)05DB}4YScA9{F9u51`6}gJz4OkJptJY?9*rYmH73DFX z9M$>ODUt7FVc#($JR>o0bFzl+?A*k10#pz;r2-dl06e+(pTKu|X(_J4Ldw z{$g@tB>>p5Q@APUc$yuPOaeLwmBs$l>C087!>qYc6@rX8a9G86vWR@@bH{q;>LEZ0Z-n$ zmBJ4aRmzb#%wmUNPXX|KT4VTE|0slDuXA<1YVK&X5HRTIWHRk6*-5C63pF$&*u&>y zvzO^VCk=pb3RIw#T?-*cAXw~s(Y{Y_pvpx=T*W()D;!D)x%Da1Xmqo?B)~qZe5`R$ zT$qKK{Rd=djVUN@pBt5D@k?R@XWB#($^M9<92~O;EH3-s3WL)BwQ*cGe6a#;ak9WFrtwjr`D54=aCBk}UCIv+M9t1y+8)Zk?Lu-bYyE$5p{nAUzQy$-1} zbu64;ON?*NvJ|anzl5?8*<4v;T0^$432s`%&d77a*fXvt+V6Bx*C;J7<^;m2#=J-X zRK!3(mQ8O&0{-+hL$_nS_bx(7nvKny)y2Yiln)gM5sJ++%~tx^owWrP1bCIRW~?b! zq-6VJy*S6dztCn)l!)LyyC>W7s9N0!gDXwt-5MhH1XI8C=xwg`=OuLk%-c#Pw6}DN z{@~yZZO0{ND>gZaHfECL{V)}(3Z$wUc!GVd-rG(Zh{wR!*UVI8e-vIJ3_$cWKF>^g zRROs~Cq@|qyL*PsB)IWdI2OFx^Wde2W2(`p%}n`kprW7%09bwiKVG}F0vpGTJSG>j z$b-o%J@3w@BFc@vAwW&tBRJ9*p!%-(F8`6h}owwX*YxiB=?I zq+q2|CbMK{IwQW<>rl~g^LGYAM30Hq~c`?fXdeKtL&3S+o@-6Jv@LMneOg- zB6i<1E18Zgf693A!wb<72t?-BsI$aP0_TH+Ss>q&PI2lPr^3k4Xl_5KIL(;toIpJ|;Cbsv`CHn-5$?u2 zu%58|LNM}zpgH9*4QpUZbRlVBszjh*i3K?7^MH|5;KukT%-jgd#_q8OG#w!|{ZlzD zd|+=b3FLRLGLQD3uaU|qPw=^9`9-k#?*exmK3$dQcM&CrNVc*T^fMI+}$O3HO;a@QYoS&&t$`6=MU$~E|QI!eaJ z$3wiIYQLvRsIbJ`d|TD&i?$Nn`QRUnE0^IqpnDY>F4m1meXR?{L&IWG78Gq z&``p%Yv{w^BY5Toi~f!sjT~9k_Qa8Vsb4YZH~Whdli3#ne1z02CF&@N z1n?Tq?ke<&zja$&BFSDp@OTMIQ*sHLKAI`G)1JU2>#p<EmSU#Tg?4fM|>e1cLEhv=){t3%$e-QHFn zJq`b(VMBXNLEnn?%?B)Pp!g+UgMhJ54*W6y@F}2yl_;|5 zFt=G#*Vk0ED8ANEiRGjU0_Z6Sz*EWvEtVi+;f7^jhm*BWASTIj-SO>?iG9EUU=p&w3NFMR`>PJ8HX8vxE;tC_R+iQI z`#SRwYl*|pF04UbT~!h`k-6rz!;}apKTXmwx)sPZljNDtrabnJW)@o#?VUI=k}#&e zKZV+VnBu7^$zoY23}Y?`2F z!1o(beAwZKOZ`68i-^A_q?P=m@J9_R7xyp+a$UVE3G|f@#WKp~0@*8$=Q4&uCDh{E z@$2g^4gnz|T#<=iex)7nDFEv|UcDx1Y*@djO(QOSy+LdsqpStE{Mq-Jv-qV#1&NtbcOINW$pU3sAlQRGV1)=eqP>|-$D+9 z(qyR}D=R9P1>*GaNzpSMhBcAcuL9I*H=X%{kR*$+62L@d6mLx+uSMt)E|v#}41J0r zK5Nd+WTNX-W`(x`j(oYj&ThthlQ4upF_NirBsxRD$7*27jIKg|BwM}vLQ@My6S4o6 zon*xV&0Rnt;iP8wMai9eLeyqNeGN%TkZSkU^T}KL3=gSFR5v=OD;$R*75J&9qws0P z%cFP+RnY0UOk%mg)hUbNT||9N4LdhWJ~Q;H6`qD2{z%Rz16k8tj&=(r2-B}dU7CGpz(p){{}l`E|+K6u)61dvg4){E9a&u-<*+M4Nda{ z)ux+GExoS*)f2LUfJ>raGU_LnsjV!T1fS?G=w2o!jE^scWyUw}AZ?XMxU5%hb3KQj zuvh{`4!rclb#-mm^jEJTX`eoGv2}_`2$a49q%6TjB02Fjf~*>Jyuax)H4KSOh2Zn= z+}n#^d+b~dzh_$93e+8j9;m7#QRyn}K50WB_|fnr2)$W0r#JA+n*quXk{R*|@rW27 zJfXEuPRelt<0ry}7i?LvwMr)Zr-jQ(--KtntM|=b!nY?R6^u1r zpeP&d*Rn#0o*GH!54t1O~CP>R1p$X|87`)2)J4{=iffEZ8sdl1m* z<~cDs8mfmLxf>F?KIfA; z1ov4q_3~!*z!)%-jinFQ59(Tf$pv%LG8_a5n&^$A7nrWszf4h8m(P(QACC1z-V z8`^CqBQIP+MS(=a#r zR?E;f)r4xDOX;gOOlvZbi`E#sCiD&&R3>xV=0<4 z$sNtWz{^)W@mMPx{$sI3Ry$snATIj85n+N+}`d0~&f$?QUe$x_1+Xxgim z%NjFD>p)+5$&BKQgu(#p83wdB%g(+RGm%sK`nN2Xs_7Wb&)lU1GZT6|xw+k!eqTHL z-;2Ii)lB{*`wu-eQrYoSumeTEHr2IVee6$})Z}$hZy%mcC1EM*_+z8O48>~}YSMJo zY$D9^M`Gh^IDSuBrMlzCKIAe;4P4)I|0hsm1p#hNMn*)yo}y2`W$C4n(m_neG<$n~x*p)M${>^jqb?@*9m{&L_1;DOZib*D z;+xake7n_gTl2`LlI*#vd|K@h8L0yJ9qub4K@p)}euH(j=`BKEpY{DN1j@!JK*X(e zOx|6pmjhrmyReTt^n`HqWJ&8*EI&~xgY0qnnfxth?{-}N^a%8X3z#cn8r?;nj<}g; zA9rVTZ`Yx#Kgo}RJ|PFH4RDZ+W)E2s;kBu%;vj9m}3$FZd` zxMp!))N-FliuaCA;1P{|Ot0$7YN&I4%^>biCUGNMR@bAMwWws5AlBOQn%6`k)H&zj z_IVG5YhfcBw;mllWPz6waxKxiK~z0@Njh=f0zRF#DL_^%M3Qkqn(=PkJ0Fa8r-0XQ z8j;Ud*y8dKzij%hDY21`bv<9(k)@;CdIqQ_53F2F@a(}-XjR}-M{|??RL>`CwmuO< zHC3!69He976uPssr)C14b8=#nFSp-xgjLC7@5aHx&7QWv`DZ{DGhCgjMqAa?$6jIa z(2C2~F&l^dlbIo)rM^5Pg|0 z+b>LByUOVM^fij<#ILQZ>rCUakLipr$K$W<%rxHj)LvD>0Q;lM#GfamzddnQ+oX$p zr?uK7tejElv{Hh&5kMKnd)EO#CkIwBSSkI?>u&F1no6nUPt5`ZL*i+!^ID)%s4Wef zYeL|x{4$T)o7dsx9Ef{2L78BBK`sY%Vmk#Rnae%Wg=%CJIoGVkL$W7!u#OSr6*>IK zTdSrHsbONH^QxQCz8=4{2Q5@U{@xwbRx^(=<&bwR$b72t5~!S%=}ezHxa-jLeLaN+ zfY_I7bE?tQTK1CuHkJLR60YWH)^^8P7DO3X_2S0XFG%AC8lE_m6+e2C;Z=LvoZV%R zhoHVmFAj(2Tw?ru*FrBtf-5%RQ!4Ed?g3a`Za>ncw>ndk*8iMsTw0gZCZB3OXUdD= z37V%B@Pfz?PEq^Wo0?*3$ zxXsz3sO3xj#X(i^yDY_BZ>i*syV0T7-qF&q@j2-i-N2F6nfdjHi}>Z0XcAn^`LTWZ z#hE;a-+9t4%pAZ%HC!}xj)ZpxYSLC*UYmY2cVGRA!qyKf(#!FG>$p~&F_e;+1XujZ z_Q%tix}p+tCkzB`q)hCqR(X1R2Mr8mER-rYBLF7X_~-UrYCf;Ec7ex^!g#viMeDqu z_Mk@Hknmh|3J_JFBEo*u%v&1ktyQ;P@zcJuac7IrlJ^S9!;v8Y?L>&v=42WxNM)7;NPsX7_L{o+syjUl z=QzcU(Q$1rPpbiQ_|5pE@@A!omwjM+=6Bf5m6a9tGU5z6g7 zDsDd^Kl=;aP3n*7hI%HD*Bo?~GKzh(pnc>myU(~cQ}PJDSS)W@+tp6}?VKRTvR8&5 z*0AtMqkUx(HU{w8%@AQWH}Y|z;51{y#?DTVz)w`yy5tiBLTKJ#U7a(@N`33q41HRY zwv{(b*q7>g9#Dp;))d#-mF4oh>oW>^+Uf^xDT}M`)Ew(`^Hq;+hmMyw-;nV{vjwS% zS?GxpyO4j@jP10&HE0XI#BGG|E8c`m%@^#WMRT{N6BRp?ngjZTtHU;cZY7|8~#=R#O zg}N=a8_|`%*5*T$v;}-M4U$Zi!#Ef--m$8332N;{-*BwVES27t=i48B<+XiWtoT)3 z<$iMLxH2;CXYTwu(+;PY@QL>T>bz{iE4kp^#X2lz9hSW<(wAAhA!jf{zr zX$;-PUKXz|4Ar{82$+n}8)~7J)Z_kx!uScy0j_HuyGZkWZ)K6JIxUqk;5p$UQ=}J0 z0KIH?t(FcqEjSw6yuTuGcz5w0&#)A8rew^2<~3tpYbpcPJHULwNmhu=@nY{XcTRSk zrz`Urh-tF1cq-t^sr&lu75<%xZpxBLMn~4@wnh;+;2YYtGiq0H4d1l ztE+g#&K)FA_8h;{`gCp$rVzb*c_-8bqiOxBVqdIo%s&Mg-CE0@-+^mbL2mHuI%HfL zW7%m2h$fg6!#+TUV0m72Gak03N;N+TGi`l7DzPFhZ^g1%A!R+r3XHg376V6FmSr7? zxmai=oEc}{yON4cmUEg`iAu+kQ;{;fJQbZB>J6d%L#&yB(>!3!c6?<~eY!FM;%6YW zk$SW7)laTf2vaddb#LUnvN}q1Dy93V=upLPortt{GGyWvgZr8|NOSl;M7OS#E72F& zlB#anK%gYV$%2}(?jGAeU4byO*zx?5+69o)`kNM=Zp-)2arBf{Kbo@ZyyprOmF6@~ zvFs3gVgR&#M=Rm8t(Poo^vrO8&e1P~#-%lai_ULJ%zPU^a(;)=QcRC)AsJxMUA5KU zs%AlM`ajF!>FKI!*ITm+?P#q`@T7e#e=pALP+o{rou~jvsHvLcY0Pzg7-q<Oa#CuYkN1=Z4UOI<;=dtD->@3ELeB(!*kE{YR3*a+%5@?d&r9Nl63miYI4ggW9nKS zzy6&)=AU*&Z_SmEZ(tWe90q+wBb6(mx;-Q z#!PtX5Xwk;7G)WZXt40>2toy2CBFi8jN+F(k84rO&8_rb?u5E%YF@?{BPPR7m+P$Z z%gP*qBIj6sN!sa@gXF4+#Di%~MzA|`%q~JEJHGOV*sXmkz)i#)a zX>cxmWjox|2E3!pI%ek~<5TfiH?XmmDp7}9wO3xjXHb`QxGY4Vd&jv5ekJLAant7e zuj)GrEIOM|v?T0@H<;nJr@bhhc6D4N!Vfi%c3-PE!=HGo*_Wv0I$;5Ws)oNn8uue; zD$h~mU}^mpJcGc;G!+aWKiSfJTT(%~dB~v#x3Fd-cY0kHyj^kH8hnfDrs{|SkXf)5 zJA>>RYFjA?qkMehx~Fm+_UQ|*4ufUNzGP0f2b}<4v>%`ByfNVs>Wcf*Tj0rT$|CFL zK^EHWM>0b{230G>%6Nxqp3bVjX3)gskcv!Uf^-@04u{A9%bxq#MRp%_m))sRPeqFX zjkp=#`oAHfEYRl0w>jP@;XGKwp8&(EPv!(08us^c+S*L#hMYzI@e4S%ZOZ~=W`H5b zhQBVZq>6>w9JS!yCaaxxk0bhW6A``?GXh^8YiD?(Ev-oJbJQMRzynW(Z8k!YKfd%A zhG~U4gQg2Ij(>85eeqm+@i>f|ku2i0llf)8ShWCzHM(yfQ`db)Vwthd>fN!IdAwr* zpwC2mS-^`WPQ;C`2O=B~2(IVP7a4nB;*G8>YG(f)4Npl3F>{3hkf4x=i~pPdG9{WI z&G+jWvmC}>YwKSSrFi#C*-V4kJOyxwh4pLRyHBEbB4lL9_1=Hcid3sHEKa|rx<08` zZL%3b84?#&eZma?7KYAkWo#3GR?DP+qiArejtrg~1*l=iD*gP~rNyuR1B*{I*7BNQ z!`9Aj0`Rgc&B3#qpd-3j^Hw0)(ENyofR#X0Qp;(RgyuyM93X)=K5T^qiwxMPx1M{d zMYt5eUl#zkrPv^aPL{0lIu@4K-I?|#V=lm4e6a1{2YVDd$~Al0+{kf-jgSp`?6>hx zfCz(wfuGE}HdK|Tz~D{n3_&QbY3eP*rM<Lf z9U}WjKNm9jW3OO#dYz@B+1bj5vddd$Mc2E~2$NrUmveCBCMe;c2vVE#vK<8UyW>`X z2RFFD!EC)xzF{#L2P@^JV#RZtHa)sb%(X)&+zC?Nl8fnKcrMtA62UwmwvwF*@G(SqB}u>roz;W#pE z0#8_FbCy=e^)^Csl<{2dXxUSEH~NetX6SV3$?e1p#is9LE-&C~r_ulhQUg0B zTc#wx!8g9IJH(K}y9Tk^KxO(T`fy81;xtVv>)pIRw~17Y`$Z zlVOKdb6J2<5g5|J3#!g?y7l&U{$ObMbth05JYm4iCV(vJ{PyAqp{Y9^*`z z@D%*~1c#sr@C$XPHGBN>Vit}4C7UmI@u2x~f|mI(Nh;Bp8zecRXDq;Hwywk!Jjl+| z)6=V36rW$U0t^TUW~)z)h?0ptB4uM}U$pCg|LD>Bpt5!#hLf+R#wYd>*eeE}q(NwV z``!XUc7(V;Te)2g=0-D`ArJlEiI!8A4y~~?zp6tD+2K55?_IX>ARr%lOylQcfrvj* zTnbS8?WbiwNLW=g+UjmBs1Fzvg8RGPTs4*ks3g5$h~&$X{R{-Z8-Yr~Cwa9umWF}r zt*ls=G%jWx-uL?>n66ZC_UowiM0oa%f3gXfDJ6Z?#zq9Ccb0Y`hBfY<)Zo7F=StyU zD2`+4LZ;nq^F8){Y2XiGnH>raNTC@1WEP2_XA;;*7~CykddR5|{;Q;WB*a5FvyGi6 z1;nx5;Po(E;a2ck*v~I&MUNm@rY_x43$7aih=aT3YRo&7*F9+qd~gA+6*sK4x5G7s zR#>gAHk$hqE@D#~gFmjZMBqh(X)oVJZY(UPvy1k;(o^iVxk?`ge>QuyTB>%OU-k9f zlFC<2R@KF3_YYg}a=V(^+$va46mr_3!^Lv9VOxHfqV-%`9`R&u3qBaZ z*zA-AzYL{&q6cD;Xn48;_Q0`$`RPRWh9^i)UhYnMw#2CF0Nl({^nAi8;}>idvU2oy zou4zgHK4Guv94nUnQN~^c-{e9-!s`3hae%c*B@M;44{$=AT&%|ZrBJuN#QUSZ|-pO5qOX+j}IBm0F(LstX z%JP6J?r5VWn0l@Anr_;l9;u2Yz<8n&xnsS0^dMJo(AD3=`=wo;p6aBWB^KubTRZsk zrf%OksjlE>2r|8ksJY};RBlTDjJ;HUJ3e7$tY>3Ph&APeY-8f*efj;yNy_arqH-;$ zP<3V=Z|3*nE0&-7Pk&3* zTFR%W5l(9fzWc`ME~9!@(u-aevECD*pxsW5b8dX_yRMzGg)MjUN){$1;~cduZoL@v zNUYx_KWSg)tB6icKKFq~p)Syb?q|ou7O){AAkl9raY7XFJP=R@{+Y9BF% z|A|j4cf3plL19EeZHhc$*PHzK_y`+|3RApg^hr8o#-r}Kl*==hSE?wF8$V0!MmpZ< zfH3fU4xGr>89Q2;mm}Xd@sfr#71SLusGku4OA5$W4@IK2M_0@S5n7Im`WMbmh|Naj(kz!$SMDtS7wN3MZDV* zdi}h)Kp@c~WskG)KwNhfpG@&=t0QuE$@22Jw_=XMJ(Z=RIEOcNO9$~QkYnwMnUy_Z z9%X&00^o+w?bCh2`(;-0m*NlWxA$n{er zv@Zz^$yUv87_n9JTE+qDbsx0d56fvEt?@#5ihsKc^R&)sSIfw;kb+-)RM$ZmK&JL~ zt(&*lqkv7ZWuAPkOV;m@IW(wpo+q`Sl|Ysb^~qY#>|j==RiLw&VriOWTv#gskDJ^+ zk8pac0!Nc|E!;%zX2K->`3FSdT8B8xjQ2<@S}yz3@XxfDi{2uenU>ifd)(DN{rf`D z5pbm|=N~5gnU!70e16$F{ml83k{I;<2UMa>UjPfYc*M=26sSTgx$GJ1>!`2WZIY#9 zRYmLFIG!U>>5C_zVr18$S>uDi0!*U_c2iQ+_zcc=#5%A3F__yulW;yV0*0|VW> zEIQNGAmp4L4xdgeCd_y|4ivRi)1B8{AnvWTrucC0nx+sdu0Q*K0SQpPeY+m-u3ZEF z0|g%FB@1IFTMNM_l$P9s4YR433oRv~UYX=_F9?PgRa21F_0pRxUkWf`UW*~otNxyU z^ce}31p1uTQ$R?JWE4fL7P^<&s9&kz`a+9gyDji;y&y~Ci`=S6S(eD3e{#d#j*fqpY;C0?T6GywgQlb}-9 zT{gUHRa5gVw)&;e>KpD=_1wOQZO8Y}gJMbk1+%6nGh!C{?Cr|yoD+LpcJ9;s7kjNj{$FJgFfnW3arBVa z9ea$b^9S&s62~I)~-yDy6ZT9r;>zUz~!hWXhbzsvs>lJWD;9E?T=v z)52M+OLOi;LM*++8M3Rr~A&&`Ot4ZC^in=N1dMrR<=Pu)73 zeYRoi@Ve8PPYxkY&HMAk=y$ZODP--)eQ&^7z_ZO8LAkWpYMDRVAzu6Kr{2kD{HDp>SNtcme_NL0$s2J9?~r}~j;`|W zx_-4QH~SPSx$Dxn5-K0*->ccYT{q%4K|vV($E#17#UnlMKjSO$!Mtg+nmc}gx-8az zfdVQ?7*hClMg7(cqjZS6<~Q zsOnLL%^mPW)pYiF7@YE*X#0w{BlF(suXPmbB2VAy!xG>t;Moc@_>^G$(!hl(1{fUM zQL!CpLhR@fc7b`oK<>UNKbDh|L4J^LH;UDI+UbbMAHIzaJA*&;QbP0awcITfv#QJp zxOfle=k(HmF0U)lAapwxO3J%+5s49Ua5`4@{_}S`9P!6?81Dx_2Yvk>1R1Iv?8x9w z4V6ur%Qsk=E9p6UE37Q^pXzjQ6<2Qq~@ z=ilWVd~QcF)Kx!^-WbNsQI{vzGQ-*}?a2-%;yA~ncaf5Oo%MA*R)lEFiCx^exEV}7 ze1PH|;ARA02I7RK0m(!12M6W*Z9!6~zQVp&HH(k36AkVBkCvvs9*^Cf&NuK87D98? zrz|ADm#ee$D-DJNPVI_xK)rGh##LB;NtS=fAE*5TZjXO_xg~Vg+8W0-^4kJ@*2BDgJ$}eR9DVg4=8efh28}?cydw5eHonSed#+Wle+h3=sxqHtM90ACBb$uX$glyV zUVBVPIrl|iJZyl4zqfJ3fu_a z5y1$Z3;X)Mbest_8TCZ$P-IwN&9+4hw5bp|rn~+uqB!~VZfw9DBH#(}l|6ltmtSG~ zXyD_mY2a5^H)>pY%`k9AMmtUYX;P$!bspK9l0x3@`f7{+OlcOI?e5J z{qp#8_pUY+Sbnn(4~#GRj4rI6H6ggsfIOZb$8X3>VXp>gZ)0og>wW7rKlG|pFB61e zh%c|MW`}p=d}Wyv)HYHmzyVxFeJt&Kcc!*)JhAqe49U*{e5I}I&6kGYZ z{5k>hBX}vPwJ67PoTYnkdfBaz9V zh8R&RC;GxuccGvFziPg84+SE( zkxSp5ecnag5~1+Zme-o<=Y5W$gdIMzbjnyxRNiV?LpEDWV$wJCD4Q9C%9~wm z=c!N0dLo-WXo3o{*;`u_;-XW)KhVKiy>rs_MioVoVA~kEg_LrzEWqcE&#tEw2S{ZaPr>iTBcaSTXk z-}wnPyWVN6=ZJ;Vq0KjFxT8NcsZandqT~4|GQUS~Rr9ShU&w!PNc ztl|-i-CNG8rl^P;t+$dH>U2F4@G9>eO81dwDQmgN$bAfTS)OxMxYwrzfky{%jl1HZ z7gVW`Uooq)QUN_TwO}3Le0~zSn3`GdQbv!Q>^f>?y=XN0jOO=}RMuI{ux3t2@z5&_ zZ>C8!cl*Rah;?$)?c3~lm?Z@5_lI&!qrn?LOovq(i_V)VL%~95w-e6gCBK%ZIHV7L zyQQWVmidp(>U-dsurafv;JRu{@7qsrdt>4YN>g~%1WQ)-csQt7Kp0<~9(qTsJV@-* zF$IhtJj9=|N0AO#8N#BWE&ZF*Jpa2I$&^pT0|yYBinRKR|t>D11oD^Ca^=UuXWo z+-3=j-?Tt{{R{2~0eh2!?g}F=2` z_1z^qP3J=pFNwoZ`rB>|D{J0NM=pyW`rc6{=2fmI3NA+tyD5es2de8-l(OfONfayL zaND0z-IH2gN6D8g+ZA^RiO?Yg!o2OklgOb6PZ6QxVXuaUR4jA%BZilcFOcNPo7;bx z0az_g60MvK4#V+OkZsN@EPS7#?ln4lrZynECBv}0ysPlB?k<4io$7#~-&xBVfA6&h z{mbz6aR&M~2;||H@r!-oA2^NW+p@(3By>tQ$g|h(Y^w$}h2it*Za2QCyoEWqftk!) zmg~1cR-sq*O{<1geq--gc$g|%B)mG5J7;+pwQ2F6iKFGUlfAaheYD*qI&ESvC%Z7qlcyM@^&;b*qhs z3$=(fMg_hZ*A=HIW3{E|o=@Lvme8=@bwl!jF)@`5g&vJM%K2)IVmt8?1 zMZn7Jg(PRe8FCDAT%CD{WtqBZybaYDm*?j2TwPq?_)eA_VlPgf=y`~$X2ctfOw|kI zS$9feE78@F`mAY@->&j@nt#*d z{Y7Ad(Bj0}4fnoarGKZRKru&@j97`zDF5Uf_ul8ap&HMRs6|!;(3qZZ_{EIc*Ik>F zkdeQKsHSy;_{S|RmY%+Aq4CXCmkr0trRQ8T!lZn4t7lM<9;;TgaU$bqvfg{t-?vNC_AxlfN6vCKOlL31T>7$7QFb^7881-XZl-?P{zOb zBxKNVgCb$S&id%MKQ|KoMw^dq9Z+9PouV^G0Ck%w#LHO=J@621PjLwMtgfDy+{qXh zW7OY%aitWPc2I%^v-6jaI}tP!3Bvfni;p=zH^+@|_>!6yI+gckMrdEY45@OW=%QUj zfA8=x@|9qMDDniu-e8KyBF)TXoYg`l)5i#% zeYVze4>U`|edHmd-@kifbe>XkgI1ft_~7OI`i#r%WqjOA!~~ZLN}TI-zG^D? z=Xn9IDmXRl#@9RZX#Vx2b?qy*rR%IKxg-3ZAX3n;bEpg-nUTrB_GTNgU=&^%nz3p zTx<@9z=Z_AEqC}6IYD-do1q_35^T@}9-?O#80xyFR5! zKktqv27cM218wNSa@754UvU&qUz;cFjUOglcUo7}CdGedM|)kf?3N!Dzgr2So=?X=cVFAACIt)K40>1aP{Y$X>sdYUm_Zqk3*|W`gIwvyyM<4@!rS@WVeONtt7fS%bB0+Vnb(ZT$cihg5idTb?{oK z6Hnu$QY3F~8@51I>}(8*2S!#&4JdMfxv06QYN)mat?9`4cYiO%g#R@M%?Az)E<^u= zoj&2>4&m}I)p^Y<+&*v~X-EM$Fp32}NI&X%%o;<)Vo2x;*H=6tZ7;igaYJcNZi`No zs>NngNel`pq&#f$yDre{MWmU>c#96h#C{B90>qg6N}Xjbd#&$wR+|) zL;|EN&KA0x2lDdpFIcw3b89uTMA=i)trbQuZ^SbZPSN{4E+Ws{V}I<=fJE z%W{F)hl+HIO7qNjaDI3Go?!&&g;Y9LXF6et-doe(zdPdP>Tt3kv((HD(q7+o)>&(l z%6jU_-FvIcnuVHPXQT;)6`gqTWOZZ!UjI&-AgdW6I5Qsdu(wvtIT5?vKq)qq!R&Bd zRtWKG)60L8%l)I8TIA@-@G=+cmGidI#=}~!vRSdXv;|9R4$T(WxQ)K{`CT5rxm%Op zA%rAC3v`vqH_vq>PukDue(7b;ZQDdrV!h8P&ew>Asv|Z!?$-uIYvP<`>I-kMpFbM= zWfDR8fwE`QK2}R`94#Y9Sjs^wyNd`H&%G(JJ9nv*4+>I2BfquhZH?o#$5LcLU<~hf zq^DfnmqXcnBhwsp{Motd_mq@uWmQ}gvwd&w$e4Xf1dawiDWznL z9(4ZyQd|0AhWvSjJX$VnC6tvkyA;bq3aO2~5g{b}^bcmjC1zjL7M%=%Y4Cvv{6%w2 zpCc>3YO~notm5*Tj2cCPs6n7#s@*wqNWJ(LdL?n^69IE z!K#o^f_F0-rZ_2(0{0}4y@IXUwfhNRh3Wn%_{E9wI~!^kPK|5!{uE$NpRJ+!Gt)>X z>wCt`W6O{M#xE#*k=Vr?W3zkzvTuwIIP}r%Q8099TQOpgn?8whhz#&`Eij8ycoUf zfDv$rcJia(Z=C;QcgVhigj@(sxS0eXh1r~Qn9~Rn-r+wM#55W=Y%DijLT(7CdlN@?ZQpge=}iJ|s<={kq}Ih))em*ytNBSKzgf+Z0pc2y!Bg z(E3od3br3=AKnTObD8IZ%!t{N7I=ojKdvrq+}D!r)Aix~+b$~A2$bWfupZu(-xH%0 zS>WFC08jZ4tlA;q(Z&~gTApV6>U_P}vwpWvIKt=4z;I31t_CdNaw zEp1N-c&pI9W(*7DTiu3 z|EBq?K*d`=?eEViuFn~i&LLlSOI>R0X=;qHvp(8p3y`#Xr*?^K%>Qz4{q>z0D6Yw5 z-zi()O zG0*QQA*OGtlC73N2gjKQ1LOK~ucUlv<$Pc{7I@ryWT-kkr3&=jOVh6kICA*4~{^NElGF ztTEXs9g}#&U1PpyIN5}>c@*R3{pJ_|#A=#T9n|6ekzu|LCvKUFZ&IQ2#&SHS7cC1; z%VPDfW_msuwRZKX8Jv-uk_Pu*t;qluX$I_jhn}v48f;#^jJK$zZ{BxH5-CVl7rimi zYL9v&YYyuO;}@k|kSfi7VKN$~o!Gim+h}fB;2)PY*|JF=z|P9;3+*G9(3P;6wZ zZ+nDQyu$2*InrrxeNM3cF=;oaHffLZ%}FmaDG9)yPiJMo;ZJa?Yzn%8)P%!}wiruZ z-tLoR*>bi!;~#2hl8KRKhqTOU@tM`br@rWsS4H$JqSusDBeSJq- zB9=U?^{n>D{_@@AWO!YS$^{vki84(8%rBFw({ZT*6ooX2sq7;Ma%h=E-y`^ zRH=q)!@EV40d)(OWcvtO35aqffA~I|`h~@mO~@{FXN%;j+}8=(QTpi{2XJG}cXrC! zksuljckz)l^2s?9pPm5N528mSq~(As%Oq72WQ}B*1-hFw@a{(ab`P@ZED{<_i$Fv zh^(zDR{0#-*J0*n0kmB^SYP8uZ?NIHYKQ zxOi0pB#Q1M*2OA2mCv4Lg5x{bv%R$k&KzGdFTHm#x6k^XZt&2dKbNs(9O2|Ul@#_h z*qy$TEG;Jw!|Vj{%Z(e}7G8QOC!wToyz3%q5jKKjXRoGXeP9 z>V{GYRkH`uo={bOHP93p5ReC)uLcs^z)FNd&6&iH$g~*3={G z?+}K3o8f0ddy1o>!H#jz+DdD4sCnh6JpGSx?%ORZRQCYdZ{7r`2GpmVX{EqRiF-fyi=YNTahK=2{yNKSwx>Y6Fu#DD0-#YhrI}QoS z@F#h_Wl>YT?HL#m{81p#c~aTp>@npMft%n+WNK5-Bw_Z5WM=Gb;l3dKw7%k=*8mE*1V;l)4Liz zMKOU1zB2pJd(ZxMlUo60W3h>}tM=xo_c|24-CaUOooZaB3?&%D=q-51yaii!@d+AG zSgTtS?Os~m=My|EpV8j%@ww*sX3gCU%Wkx!HE7#!Jej86{P^R|2cs84484){n%YSM zQ8psGN#^+n2Ng%?K|JEk3>|FYzr6!8a)quByHJ}CHX~9|1wwg&>aH*|kZo6l&Q-bw z>ml;O`KG7V30t{VT(u{j@x89Cd6l`s9cN=8TSe+PDJef z<|2&c%V`@T1*zlE>wje)cIt|gZ6nP6bfX5v2u+pq)t(Elqx@*!#wkGca5^ z2pcIoo(l!-iER7%bkqY7+-L1CTNL{qF4g4L4?4u1QI9^m?iRXMFaq$F1pP9KA5m3# zox-7hDcJ0{o~BAPZ~jv``+iltex0T8uMfN&cOG)uH{oRvqC|})MB-)rD1%bz`Agv@ zRA`U0huMwIwQ=4HoRPGRbXhCOFz%S}tLR=b$lhZS8fQhjtorV*jWo9g<-sNR{c36P z){E+$!#;KS)ud>ZpDNI7_R)$Yp~f(HOH^v+x4q}n`Uk}(=`TeH*qGJ3lISL9hvzqz zbsVuz{Aw(L4lRbU z(;pNS!Ps{nk2J?XJIE!$nrl`d7D~EM2*`9?sO_VTeoMN6+wJo{PyDcOZ|TLyX}i%X)5JKH1zpg_l@)6^;O#+!nO!}nv$HB5DJkE4oV0Q%clFMta8GjhNRdn=PeaRUcNwOzT@P9qH?k)Uv%ScW45M*Vy~12@5H3Y? z{Nay!aUj8}XMtD?ZUPfKyXnhMENI~8Sl$V)5%uKhFLc34*IjZ;oU-WhR-y`LZYB8y zvYuRB0N=0#Q_bDsyM34?x2nZkBT7oh)9Oy{<X=ld2n~i9uRNDnu{V zTaZm<9=8-M!08atH++3K1^l;oR@I-Y9R>gHy9G{uS&i9hcVlfY2i=ySJdrgBq1m;e zoj5~i5|yb*m(2o;^7YzGH^){9oF}-4YWNkdtI@fUh?(sAlh6b)4PC8W$ z$AiRtiWGLf$R0AVP`~J>9u`{uG(Q%UFcI1Y^oH=y zqKvH^{6duSQ1mBlxmoqRkvsGN5CV>jSyRwx3*XKPs{DYHv%&ORn|2d%rk7b#8pnF) zn}+20uSI%WWK^~f1|PLERx>ZdA3DUijSLD+xRhE`W1)PPp!*{!06I3uGwh$(K)36q z#8Ah&5BosZkTUq4;sZykf8BN(klk#9?cG^=;)f>Q~hh|a563eb=Zr_5tia91JyFrK$! zP?4dNhcVJleu(mDK`21(=D0(ljz7fHw+6lIeQa-qg!=1)l9&=?GpumD_mYJw>?~wg zr1(UJ5Gjra`aIb62;4mVgRUI8Tm2HcU*`1r0ZWVMrH8rBy77I=P?`}3_B|H0So9eG z#jyDMlsl|=59~n0%Gyr_I|ke9nQCH zqzN1R>x*BGqg_i)rG7xiBFu(0H*_deOf(1w+zOK%Mww+>aVMi%yA&H8-V~Y|C}9oC zugDhH=uZYkwj)p3%I*m`|EnS;eY8HTR>0if#J}!eLkB5?8{zr@Ffm(s<-4kBZ0HLw z4us7Zv5BnfhBK`0CejIBPn)71c3%XAf;LUAuYn>^2Jn7W6-|#tHJ}$(Ww)Ekc*D9T zHyZ$1*}6}}PI7%u#KFk_m*WqFS>$pRbh%P{K+r`O%j&Knhjt<)^e}ZB(`+V>{OL@y zkpVgE=3bo&=hnLwB^F=~^bTZ&lSr`Qt)xHXqX!*v@}}e;HoV^DVDdgWQhSZOBNoS$ zRN9(f;P!;vC_AK>4>Aj zZ!Lk3Q`d+}i0SE(nBsyh}7W-{8Po`56o0!O}N-EYrnFirN#ws-^jUdSM~${f_&&X{n$*JyJ&Em;-TJ5x@C_T+dm3|_NJ3E6+GTo%}w4`lRsd+DARx_8Ew^f8|9OhCu-!_ z_BhUs;8^5Md;hK)Q`>-F8kg^VKoghm!4GtX9b6leJ^HvsTf&|Fg5&XShUNsHV0Zz# zix|!nYs>Jzf4v09)C3^OiF*X1m?Cu0?CV~?6|$TUlo~E;Kc6v^@#5v&cDZCuIdEi} z9g)yd)JeZLYUEWyprFTuT`;-+nRdlFb<2Ws6+?3KILY_Gq+GTfdpmX)>m$aOs*dJWuqlTcvZ7Z$BQ#i`Eyx>kTvQLOLz zlgmF==4O+LHch?r1YpS98We%d4YLWUpGe`O z8w-2<+6iP&z7BpvM7iuYfhI!tDAYPO9K7AP-@J}bVGX1}yD&1H?pYMqr;270sJ=%c zKyy+kG}I9f>D7Ktq`SWcj3!7U63X)2+9_vhR_>~Fsk_V(>EcJFl`*Z$!Z3n>RLZZ( zoexlUzLL(dN)BuSUBr4{_xO#K9R21pFQC1SR~XJJZz_A2xH2Q&OMb1kW$Eppc+EZv z^H4j{Xs~+FeX+@UXr9!9QQ-zpSbymA%F4fFIAlUrQQASWUFK(A6SDt z$5CnY&PM+P3*5YQ^jRknV~Z74og)mI5j=F#x}(JpQ4NhcTAEAmN6=vX5Chw9%ZB&k zRN1j8_A|Z=RdV0Y(<95?Tx1Kvz~Eq1|M^wYI)PaC78O;FHWtj&;y#`!^m$WuNfDOH z$UriT%+?_dV7Q1UL;^rb?YS{ByHsNR*)P{;OdxFW>ql-NS6JG-UltsbId&D#LY5x~ zko6!kVi1kR?gAL29mFhG?A(=p4B%0Tsm;Q0TAV4(uZVXGl{Ju#a?f5ZWLWq?`r<5p z&AO+}Yfs1PTF&fW!1jQNyeGU-k`a2LIfprDTE_b1u%FXCFVqt$59zH@ep?KrB7UVp zQreRL;xSt)u}_9!&+aMIsK;y+@&ik;$?&gA)JFnO{)ESx&fe!Ay$Ic5#h#3Vsf|08 z6!=og?hcOkaI)0c=BAV9Ar)!4s_EQ~{GAtQw@#~RNGx6DxtK?r+kj$rrqNG&kW2}X z_c~m^#T^+R>XsqEINi&h?9T@#5zV@3DVg4KX>MK69~pw+WnGYY*;FZQFFzUwX;u1eFLxb<Gy1dMJ!Pho zEEO6`&%BUH#|JeqVv6;+QxPvxeCc<^#aFFz1&>{Rmcl$dsPV;dZY^yMCI!>jk-5W0 zC@6|o_JPv9zEtV^)rb8IKteY5vbl$W6WjEw5Y~1i%$PHdL^!Xy2y>mZv4BPuPC^9t zwd2*D0wy(NB}i}cN)wVOnS0Dc3a8ZbHCQ%6I6_0#tk8GmtYl;kb9a2dd(eB&bbeebbyEAnXV6BAAK^$X`SZdbC$?3tPhFe z3dP!$2P=z}dycG(a=Gg$yBa|qu@E}McYFVK7d9~~qmtg*Rr_G>#+0PK#i=CxvJ7TWpmzC)>RuZm;bJs~x{}?dvUX_g zR#Ya+i2m$MDVhH^fY^sI5Ul2H#4@K~1tWTFRw-}B>8DX!rB|VX0u++FkV_*UD&_I5 zNPKxNbUhPPPNzrK3Y+I)8Ak*TC$!hBH7sd!6vplX+~_Q7Qgw%OWgI9?CkaWPeSp0l zkAc)|$yeD%mVOh}yw}x`?}1Dp8Ecxvh|WGN+y3dMTM&K&IcTNK9Pzh|>rs>IAI=aQ zq0D;-|1LqRM6^my>EJKceEYMs&!=;?y8aOFQ(~`mwc<_j&*`izM&P9ovgrhkL&pwY z3ya8N(Ct*KKH3b<0XL(@yn-p=e;h|GNh%5qp7p_}%Arw50M>4kj{Mx(nnCYV!B4x< zdkZ8)0d_vqIs+FeCThf7>9mktP>v&t;ul8bw@-SYN17l%P%)!ozh!45uhGbL@6PIP zx~3bx;1>o})Y^GZ10200$xR^o39G*P9oS*#drbio&08;DShJ?y7q5Z_6qZZb<+}O( z`Rs?uMfGPiY~CJvf+j8CJ7n{8^tnxkaMd?!fUB^ELc=d?zPCblC&(>m6xNTWLcUE7 z5vdqp$G{Jkyg{RSv3+XTf9_+|aR?1^s6|1QEJx{M+)e6{YuI|v+ivSMmn9ot%mMSspNRJ}jhd;IJDRPcv&F}0o6ngbJ)UE7q@V8{E&o!Qf;ETC#@ zw>Wn<6~joBHtin&!k;cR{=ShFV975f)}Q?*y}e*FPYmn#?`ZE0FVw?l6qLf3)ch)u zCz<%2w0?#$BB~ z7eqw#-voDbkEk6kQb8>+oLf(p@YTKbtQ^79H%Xwv_;LL@^sLR5KR_jgV>94u-}*oH z6oVc*$!K|&&P-K+zRc%E7&^NyGfJu9*OR8syw3-phb;^rmob~i%afIcm-+`o60pU> z#OJ)f0Ec+g4AVVECfX@ge--2JlN{@qs5i;ujIQ-Myz(op#r1K-+desn+5c6z1=Z47 zAlr@2|9PHgNCiB!HB^+9(GvzCH;?3ebCN-f2H!#*+tgST+{nma)8aS2mHy!CSmjaH za?D45PDv76^E@nSkSxUb#}kzuiTxG|_;J0@OF#`|4s8#2d7?3uJkjxBwvUHc@in<+ zS$+FPTpfrnc-}8MD*hF&d~rKz(G1U~?{kngQ=7eS=`qW#GD{+ICGf<6N|Q88$a z_i74AfzyYmld_-DiCZ{#~+aa@r7=>i6&WvDaB-DM)-oaH7?gqKY zlG@|0A_Xk9GxlTR)i?4MHF33fHuxbtpqANOt11p8s?xuW)MIaxCYmy_{uXYz${Ypt zYgWP&0@v&;@NG|@P;URbCpag_T|~CB(#M_aLL(3QsDqV?xv2@j;tcj3TJTUmU)PJt z(FckfUtrClRGVqWdILo*Ne+Z8zyZiR(N1waQNeX-%jjm?9mG}F!qRqEs5^_;MwH83 z{1ytY)3XcrshbH7pBYAx7EFDjl{Jkv&1N}L0#^`?f3_hZif3U7Q{oMF7t6d=qS9!x z&#K04O9!Y7RC?ES#SjlWEnC3l9U3>Rr-KP5*R*$63QyYBXa-*;`6u@?dR~Y3`i9`X-2nIRdcbzy`Yo8gQVfjJsGut)hr*$W2mSz4M*0TUn}GsdYRO z3gt~%lKcGq!v-yM?|q2e9(0vXH62eL##Q)73ojQPPft%rkoD`NHjfEVpZBpD8%AHe zgA>kfEMUJc-O`>Q=HF1D9*1^A7Y1nL<}E{|M)D-iKrPIX*Df{JAuEs@2L=a3H?zmM z7qg)bdGY^?b)rOrPGUfXDNwI!JzAVPA@=}Yp=YL75l12{;lbKFWPL%q7A$a-OXWb=`)JDljAtdB|owaZ*og*q)${Iw(f*O&}b|9!jsSvF(6 z*2a>4lV0LeFVEqh*%Z)dq#3+#pFg6Ak%&(?EmNoikLTGnizFK;*R78&T88gh5C(W( ziks@Lym0}H5Z=;~aDI$NBTk!{7;QZ{&uByn>fkh}aaO8+Qgj2nqQW06@5rI89z-hx z{1O8VM`D6OB@m?E-;?cWcA0Nz?3fw{3d1L3I(gQz2>DvZVmqrTD~-vx3WrYUFtBXv zZ~Q+M1zoq(4){b{xh;FJ4=(~scQA9Nv_#glG&0otjhI>Z!cwliNJ!}Q3zHx7&M!S& zR@Z<|Bf}PCPmfD6?bCFVdHb(@xc_C$dEUykYbF<$2=lUzF}_)BvDqy0REyI!+oVuv z$9#)rr>ulPe6fCZkq&jltWe8M;Oy$phUdOBA!h72>xY0(|K;)WXW!`*h#QCZaTr-I zu{PWe_w4a|W3rhZJ!J%iz^ZL4FkPXjg_O%{hp81ac6;ini`pNO>eOs2EnlqrprC%? zFy;8|4{iLx1T2B$k;HVPD3?HY6=FX@NTzd1u}o(%m8mJE7bvS7O;*AYh_Kkx&?I#^ zGGLn2I&_*$(aLTDTlc!vR^z!k#x?ma-Ppz8f&qB2OxI~f!!v>-_iLHuJzcE_=7U+@ zeNy3j%MBil9!lYe?{wn7zI&sVm-B=pJ>{ccuDIt)43&8D0Q&-Y`qK@28B;I$B-#&L zGY%qG=6&yceWm;Ex2`U3DLfx+R0(sjF+Gv}UJ%;xb|+6aol<}KZmkljGvtq67Uo5S zkOVH2%LblPiD@f5_atiD!l*P3s;E+fUi3ZCAFyrTThHdHio9WmIwdvWFx^$b!cq5! z4R$UfCbXpdAXSb)wx%ySwi9})U;py9m@zFBIfX0`FQ*aKEVDUxCoz{;`D>~=Dk-~A z;9MmSW|g)h<;la9&+Cd_+N=bIX5$A?imIrKeNG1L@RBfpgrzX)a_6v~k44a)>fCh6 zCEu)C7{^@LtwEhS1m&e4L~bf|TUoXsn7S*VgaUy#fLLz0$5*&B} z1>oNn&EQ@&D4}2E2=F&{taSG4W|1#v9Qwx++1P+=v%luior7* zIOa6$ftJ^ui85qug_V^T^CV_j*El~`g6zYGrjwW3KUBA1Qo+PBB=UUM z_1fy z0wZ7E&t#?6sdLu&Lq9oC76+ifVZJ$%qXKf9JSUSOSU9tEy_!N@?HIpB*boP+u2Bpg zy5LX8xS!#MzX;<#487Bbu#Jg@d5405jg@Ro$3U%cSqVJxY5&t$M@o z;cX-ufIl7oC$voW4QjmdXj5Rni6ENr)zy{UfOw~cf`BuJLCo%=f;@RpD!!2^#>E|= z_~C6aKgZ|TVA#&a+LM_<>4!HMhJ3)51DL;@xoCOr!SXkYq|WoBzEYvu*90V=!nf|H z`(Zzp0P|0&Pa{$akIpXLdV51mpNfaZdK_Z?xZ3PuQSKci*Foz>I4ov)d1exVim9`Nu4f-Fv6M1CV1kf#+?qn67#ucW6PE z>kRw#OG+-3BK(psjpvx6M;Qkli$`^vW30Vi20wy=P?I>h?Pb!2%ti$2*pU&tstMWT z0JqtS#3GE%=;0CM$yAVeg%FjYF61=Fjvyv_5&DGzPz>iul9IEK4VvBhibNq122zgT zu~$9%S^H$W=DEq00k_c!nihgwuGOB%{`mDHUyqE&C`3P~^<=O6k1)&_N>e*UCZ@$n zaDfVN!XV4^T$hEC0%*s60iF4y+_DACHHZufuk^FE{vPKxFkI3mUms)^U|-+}%SvBS z6@7FUf>e-9l)#x*If$QI-1j#Hu%E!NA7iVf%Iz$mpEJp`NWrR`UheoUmHR?nOGJ;y zLH*f6DXNFF8*bQ~aBev2^S*7zgLM2Ldq!*XF=6CV`|yXd#G_;mfk;w?Dv4N&84gXz z982)3ujVvgAt%gA$O%V2tw!iui&YJV=ne^Ub}aXelu^8Gr)3>zLMuPhUowhgNtb{0PG zS(PkE{_DWWL}WCTWQdQC4;L^xT+6E+Wv>y(4g^i{j|AE;S%9RWhjxs4Yh&ObIFyW( zyqD^W2nF{c9M)Gc_%kM;nWjvg=66FRKQKu2!Lhc3#wku?J%0I!>U@<)HD#WjRYsfU7k8c070FY7=GDNSmlmv>3O)G4-y!d!G)}Zsd$W`J1G>P;|Ej0qgBCF>hxdsVM0?xt+~RB`^LO(n7N4Bw7aO zUgUsQpN&Nm!?%h7*D4Xv1{@v@)9CNE{SlrpS9p6d3V2IHxYXlCY7N_ zT1MpEA=TbZeQ}I^acb151JBY3i73hJ%aD)5QzhqNXC@S_Df;Af@=?`4(@+2jAe;5+ zBnsa{fsk-JjccDz+~L*79aERCtF)qUTfAk*ET;e_N#VBZw7?YIL5 z+pULX8T2yy@rd+4_tyCUMHDER$=5Z5htOVYOpK`m@wJw}65-f`4QqG&;d^_=9H^+|;4HDQm5CVS+ zSu51>1a`N_U?{Jrl-Nxw{T6hxrvrz_+(!tFOeu_=J7XICvcbJM=`9u2 z%58wj$aB6{#@p*mPI?T{D2?u;tl1(p_7`@5G?JWV*e3NCg{%MUB8O*0K%NA3du6*G zp89b>MY_X_T;s!h)7o5MXOUN*Y4S0t!E-l7sMFg6wO#BdPyd5mMB=_`D=Vvv>4}Mn z$)xwmF9_q>+!ASr5qt1P%IS5&cm*h|miivOSLuicm<9eIx z(qF{E%UR+-l>C=l3=Q|b>Kouz#Cv(H3_Y!R$nN?~lsF=%IG%a;%8`I*C@(Yz(Ga5* zw(YdsjHIWqShXAeaXp!xVfYSO7GHeK=&w3sUqmoa&L!4-SwHPk$cn0~E8<>1{Zup% znfr|uNosoQsH)SHNilio>)Nc#xexG^2!`4b;=u)#j{SHI6=fey={+ zZE};G?~Vq9?a@L$ummWH%62E=BJszDdC?KEwJr6XAWpd}9g&*0&jhx8Z-&A&Ala<< zW;Xl+iFA9K=9f=zAsZF+pq5YPB&qD;wc-xpK&H4RnX$~p3?a0d!|JtTDpJCvBC1PA zMI{Nos)+a` z$5BG;;cA^b!XJBqSyxDd6Z+~BtV^c05~kNOiRVb6P!0Jtj*Q7eBVW|b%!=t#hGreG zER0RH_$V@#5vrLfO|KXC)X^_+!CKGPVi# z%GzxcHIuDq7?H2Rm9gmyE1}`ljFgEZu2xktJ;~zinLa4*vM}*q{Ir0k`jYi4+K)y2 z5T5wfOZ>nb9-fTcdbV@3Vd#~bS|!+YGcC^DSM>3?YHF|-)4LB#WN9%MRja7=D(zvV zlkfYuT2`U9ERX2asADf2b|*f>=PCLwn&qV|iEY-M`U=F0sWrKA?x?_rF8()1&x&@4NH-$c1O~!Nv#`Oyw+hY2qt@M1 z-=Uno3^dw*e;VP}k0r;t&qC!B!6bKYbfGKNJ6!(SVhv;NZa$y>lEb5Ed*Y=plEg2; z?Wr-QXgs3! z^QoYvR-WLZ&++%2R@?l<1m*|*Rxj1A*t)v9TpcEwn%Ixel-2?6vqf@kdF{h`R{8S} zFWN1pa+M3*{?t()T>#qL4s8{E%}S{;)9aD+{Dfw+s@iPR<|ihX0#7ErFHrL zG56MQQAKUr@DS1n2uQb-bazNfBPm_dF?2T)(kk7Jbc1w*(m8Z@4&Cu>yr1`epYQt% zzF+1zX7-xB_Fi$VE6?*WI?&j8KPMH;ByIl0eP~4DHvY9N?SPSK(m^v($h5Pf9Uq65 zB>lCWy&8-7g4WPs;QE(zG`oktdU7Le3_!l+BK7pw=8_t(Wagms4O8>f^*jxoxmvk? zFN~)pi}FPH;Coht{eFdx-BM%}Q0da=ZVbPBPT!M-!qbAs2zRCafSc7laL;A}Xw}T1 z7@49k;Nh^5#IKYSAvrnBLQEY;+aFcxXBi~pbK_YyZGfHy8MoMFxUj&%$;MNcg-?cy z0y;8~5$nMHc&*Mrdh=8n!557G6h;;OSZU%XLT^>%W&h)S%s1iuXe6EHzGJfG z2JaJ5!fi( zO6?C^l{gyl%ZZPEBfdJ{Ukd>njoNF8B2i8ZXD7{1moicue>hbwM02 zL-e~=Tb0I7hMTzg)@|#hCO9;^AHMl5!8ebjok4O0-*c==GKX0)|0_$}yh>t_*;Om= z`^0O4>1#ofis9npGQ z+@9V0qMN9j z(1JKa5@)j7e!96*&~v?-OY2aWW7hhdq@6pwGHMB3Rc{>}m4t|zD`|RgcYVO<;1N!o zlTLR>(0X<=d?2voh-wAHe^tY1?C0G4);*ReOMQ8ueSB71T_l&h7+~}ByU)%W`b?M^pu3QwVx#1i-q=J=huR>l{OVgk9Ci^-1fg}z+moaRk9WWXjN3ws50;Eaz z$PI+qzxL}e(=o6qh7u%eIxLrFZi6phRb-&+kQgd*7JmrN>@A-|xCtk~BY0YUWesGrnh^ZTVJnUQ<& z2jb?D5yhGUXaKl*>M4}1kJZVE`F$7=SR=HJW|miM!!#cxy(~^7U%H_TGWeCb!gB=w zJxmS4D5@veeAHu-$YPM;>g){Pd|w@f+RWz^?JKAJ=TVlPe}D^oEKAWd`)ftSJXiGD z*WIgsJqSa%5#M}`5Gd(Q@#H`Zwhfhm7X=D_fs+%a<3&_@pZx9ZUQqljJMW#DOf+hH z$0PuxG$Td&SwpJ41J8}{_Y3cieMm`HretG&=N|t75Tf|YE<}bb1=b4c>!W$N#JX4) z8v52Xm|?{4=_pVUnLvJ@7aVF{i>%*_j#wW2*7RVu&J_{+YB^Gn;PLDGLdIBNDYm}dgOT778{b7_+Jos8QVGb2oZ!&nF3tUmcpW*BS z9{&)Uf-WtQ19M~;h{#go-bDb)jKgfpKBSWNfYWEbOGnt*^Lj~6;O&Ez)m!{Gp+cqC z3ES)-3tK1}EJ(wb-clS0vg;zGaeE-FMSJC3+YB{1$0#Q8Q@A>^c@m~a3Bf($Mrb0a z%D*RkaOe;CecUa*CY>pa9P#vE-{NxRi&XpLxL@qBd8+5p=v6G?Pwa0t`eN6sP|{}E z&$I0;uo*kjwOAkBZkKEf)zBWU(0l`Hf{jq6BCBO?@cP#o9O##u^V{wohoL`; z%;ioaZ8>p-@gq44;u7;OsKU_dEH4c30dGTYE|mrR1d=$3ok%R{_`34*G2vS@_SmCb zkUQW**cq}EIVIbvO+S%CzYJsFCd{8g9vXsZ_s@BAA#9MFgf%zm0x0WHUB|2T!l2YA z20F#Le4;{$fxP%A$R_RMo@L_V9d!4YM&kP@Z1QQzM%sPgi&ASjK5W~#n3wreg`-8m z)mHzDO4?HO$ebgXZPQOB;j*Py-p2> zDP1Bh%V3sWd!F3y$2lDK*X~XF;vU8}(ksYN_i;mk@TPtl8_U`u75{Q>owjnObg|4z z$zd&|ukivV*9vbY@lhMkkre5YDyHXPrCdi`}5sI=7!R z%tD5FHQ+a5YLgt?O4e=P`}51sjqAx4Qfgw3pBv-ywIG;T$RrNH-;8~T6Ku{rhaR>|JgOsZ(XPHgyhrzgZ!8%u0Ipvh=0ABu@_D@wa zrI#yQ@_>h<_rUq*Ma6wry4>

fJ+(iIcbT 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 literal 0 HcmV?d00001 From 031d81794a8bdd9f838c67efafc8df939e544494 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Mon, 17 Aug 2026 14:24:30 +0200 Subject: [PATCH 04/19] Show the logo in the README Light and dark variants, picked by prefers-color-scheme, so the wordmark stays readable in both GitHub themes. --- README.md | 11 ++++++++--- doc/logo-dark.png | Bin 0 -> 66522 bytes 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 doc/logo-dark.png diff --git a/README.md b/README.md index 0313754..a0f29d0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -# Example Infix Spin +

+ + + ExampleOS -- an Infix spin + +

This is a *spin* of [Infix OS](https://github.com/kernelkit/infix) a tiny skeleton repo showing how to make Infix your own without forking @@ -76,8 +81,8 @@ brings its own QEMU and Python tooling. Tests describe the *logical* topology they need (`topology.dot`) and the harness maps it onto the virtual network it starts, so nothing here depends on real hardware. -The result of the last run can be rendered as a PDF, branded with the -logo in `doc/` (swap it for your own, and see `LOGO` in the Makefile if +The result of the last run can be rendered as a PDF, branded with +`doc/logo.png` (swap it for your own, and see `LOGO` in the Makefile if you want it placed differently): ```bash diff --git a/doc/logo-dark.png b/doc/logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..fde4099634f0a5c50b641bceb9c6eeea9217388d GIT binary patch literal 66522 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 EKXG2;>i_@% literal 0 HcmV?d00001 From 6ae8a016232108ba8d6e841d0fd858930c482ae3 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 19 Aug 2026 14:32:32 +0200 Subject: [PATCH 05/19] Attempt to make free GH runners build the Spin Signed-off-by: Richard Alpe --- .github/workflows/build.yml | 30 ++++++++++++++++++++++++++++++ configs/x86_64_defconfig | 1 + 2 files changed, 31 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2126dc3..cbe55e6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,6 +42,21 @@ jobs: fetch-depth: 0 submodules: recursive + # Keeps codeload.github.com from rate limiting the build. + - name: Restore Downloads + uses: actions/cache/restore@v5 + with: + path: dl/ + key: dl-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: dl- + + - name: Restore ccache + uses: actions/cache/restore@v5 + with: + path: .ccache/ + key: ccache-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: ccache- + - name: Configure x86_64 run: make x86_64_defconfig @@ -60,6 +75,21 @@ jobs: if: always() run: make test-report + # Also on failure, so a retry keeps what did get fetched. + - name: Save Downloads + if: always() + uses: actions/cache/save@v5 + with: + path: dl/ + key: dl-${{ github.run_id }}-${{ github.run_attempt }} + + - name: Save ccache + if: always() + uses: actions/cache/save@v5 + with: + path: .ccache/ + key: ccache-${{ github.run_id }}-${{ github.run_attempt }} + - name: Upload Images if: always() uses: actions/upload-artifact@v7 diff --git a/configs/x86_64_defconfig b/configs/x86_64_defconfig index 7023753..5a61a8f 100644 --- a/configs/x86_64_defconfig +++ b/configs/x86_64_defconfig @@ -3,6 +3,7 @@ BR2_x86_corei7=y BR2_TOOLCHAIN_EXTERNAL=y BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY=y BR2_DL_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/dl" +BR2_WGET="wget -nd -t 5 --waitretry=30 --retry-on-http-error=429,500,502,503,504 --connect-timeout=10" BR2_CCACHE=y BR2_CCACHE_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/.ccache" BR2_ENABLE_DEBUG=y From 35042c19876a653cc55dda6a0227e5bd43350292 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 19 Aug 2026 14:51:17 +0200 Subject: [PATCH 06/19] Build a PR commit once With no branch filter on the push trigger, pushing to a branch that has an open PR fires both push and pull_request, so the same commit was built twice. Signed-off-by: Richard Alpe --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cbe55e6..327f3eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,7 @@ name: Build & Test on: push: + branches: [main] pull_request: workflow_dispatch: From 58f95a77f8d7757f5164a332ff3a4e5fa24a31b1 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 26 Aug 2026 14:48:59 +0200 Subject: [PATCH 07/19] Add example-hello YANG model Describes what the hello-world daemon is configured with, and what it reports back: an audience to greet, defaulting to "world", and a count of greetings so far as operational state. --- yang/example-hello.inc | 7 ++++ yang/example-hello@2026-08-26.yang | 51 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 yang/example-hello.inc create mode 100644 yang/example-hello@2026-08-26.yang diff --git a/yang/example-hello.inc b/yang/example-hello.inc new file mode 100644 index 0000000..80b60da --- /dev/null +++ b/yang/example-hello.inc @@ -0,0 +1,7 @@ +# -*- sh -*- +# Model list for utils/srload, same format Infix uses for its own, see +# infix/src/confd/yang/*.inc + +MODULES=( + "example-hello@2026-08-26.yang" +) diff --git a/yang/example-hello@2026-08-26.yang b/yang/example-hello@2026-08-26.yang new file mode 100644 index 0000000..8e4c394 --- /dev/null +++ b/yang/example-hello@2026-08-26.yang @@ -0,0 +1,51 @@ +module example-hello { + yang-version 1.1; + namespace "urn:example:params:xml:ns:yang:example-hello"; + prefix example-hello; + + organization "Example Inc."; + contact "mailto:support@example.com"; + + description + "Configuration and state for the hello-world example daemon. + + This model is the whole point of the example. It is defined here in + the spin, loaded into sysrepo at build time (see + package/hello-world/hello-world.mk), and served by the daemon itself + (see src/hello-world/hello-world.c). Nothing else needs to know + about it: the CLI, NETCONF, and RESTCONF are all generated from the + models sysrepo has loaded, so this file is what makes + + set hello audience there + + a valid command."; + + revision 2026-08-26 { + description "Initial revision."; + } + + container hello { + description "Who the hello-world daemon greets, and how often it has."; + + leaf audience { + type string { + length "1..64"; + } + default "world"; + description + "Who to greet. The daemon logs 'Hello !' to the system + log once a minute. Being a leaf with a default, it needs no + entry in the startup configuration to have a value."; + } + + leaf greetings { + config false; + type uint64; + description + "Number of greetings logged since the daemon started. Marked + 'config false', so this is operational state: the daemon answers + for it live, it is never stored, and the CLI will not offer it + for editing in configure mode."; + } + } +} From e299a11af521a4dabb4f2e13ccd6077b75468848 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 26 Aug 2026 14:49:05 +0200 Subject: [PATCH 08/19] Load example-hello into sysrepo at build time Installs the model and hands it to Infix's srload, the same way Infix loads its own. Ordering matters, confd wipes the build's datastore before it runs, so hello-world now depends on it. Follows the pattern in infix/package/confd-test-mode/, a package outside confd that adds a model of its own. --- package/hello-world/Config.in | 9 ++++++++ package/hello-world/hello-world.mk | 33 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/package/hello-world/Config.in b/package/hello-world/Config.in index a50e387..9014753 100644 --- a/package/hello-world/Config.in +++ b/package/hello-world/Config.in @@ -1,6 +1,15 @@ config BR2_PACKAGE_HELLO_WORLD bool "hello-world" + depends on BR2_PACKAGE_CONFD help Example daemon for the spin: logs a greeting to syslog once a minute. Started by finit, see board/common/rootfs/etc/finit.d/available/hello-world.conf + + Takes its configuration from the example-hello YANG model + in yang/, and publishes a greeting counter as operational + data, so it can be driven from the CLI, NETCONF, and + RESTCONF without any glue of its own. + +comment "hello-world needs confd, for sysrepo and the YANG datastore" + depends on !BR2_PACKAGE_CONFD diff --git a/package/hello-world/hello-world.mk b/package/hello-world/hello-world.mk index 8dc898d..d694e67 100644 --- a/package/hello-world/hello-world.mk +++ b/package/hello-world/hello-world.mk @@ -10,6 +10,15 @@ HELLO_WORLD_SITE_METHOD = local HELLO_WORLD_SITE = $(BR2_EXTERNAL_EXAMPLE_PATH)/src/hello-world HELLO_WORLD_LICENSE = MIT +# sysrepo and libyang are what the daemon links against. confd is here +# for ordering: it wipes and re-populates the build's sysrepo datastore, +# so our model has to be loaded after it has had its turn. +HELLO_WORLD_DEPENDENCIES = sysrepo libyang confd + +HELLO_WORLD_YANG_REV = 2026-08-26 +HELLO_WORLD_YANG_SRC = $(BR2_EXTERNAL_EXAMPLE_PATH)/yang +HELLO_WORLD_YANG_DIR = $(TARGET_DIR)/usr/share/yang/modules/example + define HELLO_WORLD_BUILD_CMDS $(MAKE) -C $(@D) \ CC="$(TARGET_CC)" \ @@ -20,6 +29,30 @@ endef define HELLO_WORLD_INSTALL_TARGET_CMDS $(INSTALL) -D -m 0755 $(@D)/hello-world \ $(TARGET_DIR)/usr/sbin/hello-world + $(INSTALL) -D -m 0644 \ + $(HELLO_WORLD_YANG_SRC)/example-hello@$(HELLO_WORLD_YANG_REV).yang \ + $(HELLO_WORLD_YANG_DIR)/example-hello@$(HELLO_WORLD_YANG_REV).yang +endef + +# Load the model into sysrepo at build time, the way Infix loads its own. +# infix/package/confd-test-mode/ is the pattern this follows: a package +# outside confd, adding one model of its own. The SHM prefix has to be +# confd's, so srload attaches to the datastore confd just set up. +define HELLO_WORLD_INSTALL_YANG_MODULES + SYSREPO_SHM_PREFIX=$(CONFD_SYSREPO_SHM_PREFIX) \ + SYSREPOCTL_EXECUTABLE="$(HOST_DIR)/bin/sysrepoctl" \ + SYSREPOCFG_EXECUTABLE="$(HOST_DIR)/bin/sysrepocfg" \ + SEARCH_PATH="$(HELLO_WORLD_YANG_DIR)/" \ + $(BR2_EXTERNAL_INFIX_PATH)/utils/srload \ + $(HELLO_WORLD_YANG_SRC)/example-hello.inc +endef +HELLO_WORLD_POST_INSTALL_TARGET_HOOKS += HELLO_WORLD_INSTALL_YANG_MODULES + +# sysrepo keeps each model's datastore under here, group readable so the +# CLI can get at it as the logged in user. +define HELLO_WORLD_PERMISSIONS + /etc/sysrepo/data/ r 660 root sysrepo - - - - - + /etc/sysrepo/data d 770 root sysrepo - - - - - endef $(eval $(generic-package)) From f06906e4042a69bd54097baa5876d2e45bc5a882 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 26 Aug 2026 14:49:11 +0200 Subject: [PATCH 09/19] Drive the hello-world daemon from the model Two sysrepo subscriptions replace the config file: one to hear about 'audience' changing, one to answer for 'greetings'. The daemon now waits for confd before starting, since that is where its configuration comes from. Drops /etc/hello-world.conf. Carrying both a config file and a YANG model for the same setting only muddles what the example is showing. --- .../etc/finit.d/available/hello-world.conf | 13 +- board/common/rootfs/etc/hello-world.conf | 5 - src/hello-world/Makefile | 3 +- src/hello-world/hello-world.c | 132 +++++++++++++++--- 4 files changed, 125 insertions(+), 28 deletions(-) delete mode 100644 board/common/rootfs/etc/hello-world.conf diff --git a/board/common/rootfs/etc/finit.d/available/hello-world.conf b/board/common/rootfs/etc/finit.d/available/hello-world.conf index 3601232..2807478 100644 --- a/board/common/rootfs/etc/finit.d/available/hello-world.conf +++ b/board/common/rootfs/etc/finit.d/available/hello-world.conf @@ -1,6 +1,7 @@ -# Hello World example daemon. Runs in runlevels 2-5; the -# condition delays start until syslogd is up, since the daemon logs to -# syslog. Finit supervises it and restarts it if it crashes. It is -# enabled by the symlink in ../enabled/ -- on a running system you can -# also use `initctl enable hello-world`. -service [2345] /usr/sbin/hello-world -- Hello World example daemon +# Hello World example daemon. Runs in runlevels 2-5. The conditions +# delay start until syslogd is up, since the daemon logs there, and until +# confd has populated sysrepo, since that is where it reads its +# configuration from. Finit supervises it and restarts it if it crashes. +# It is enabled by the symlink in ../enabled/ -- on a running system you +# can also use `initctl enable hello-world`. +service [2345] /usr/sbin/hello-world -- Hello World example daemon diff --git a/board/common/rootfs/etc/hello-world.conf b/board/common/rootfs/etc/hello-world.conf deleted file mode 100644 index 6f293d1..0000000 --- a/board/common/rootfs/etc/hello-world.conf +++ /dev/null @@ -1,5 +0,0 @@ -# Configuration for the hello-world example daemon. -# -# The daemon only checks that this file exists; parsing is left out to -# keep the example small. Put real settings here and read them in -# load_config(), see src/hello-world/hello-world.c in the spin. diff --git a/src/hello-world/Makefile b/src/hello-world/Makefile index a32ac3c..d256985 100644 --- a/src/hello-world/Makefile +++ b/src/hello-world/Makefile @@ -1,10 +1,11 @@ CC ?= cc CFLAGS ?= -O2 -Wall +LIBS = -lsysrepo -lyang all: hello-world hello-world: hello-world.c - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) clean: rm -f hello-world diff --git a/src/hello-world/hello-world.c b/src/hello-world/hello-world.c index f248c81..52b4ebb 100644 --- a/src/hello-world/hello-world.c +++ b/src/hello-world/hello-world.c @@ -1,17 +1,38 @@ /* hello-world - example daemon for the Infix spin * - * Periodically writes a greeting to the system log. Runs in the - * foreground, as finit expects, which handles supervision and restart. + * Greets an audience in the system log once a minute. Who to greet is + * configuration, and how many times it has greeted them is operational + * state -- both described by the example-hello YANG model shipped by + * this spin, see yang/example-hello@2026-08-26.yang. + * + * The daemon talks to sysrepo, the datastore behind Infix's CLI, + * NETCONF, and RESTCONF interfaces. Two subscriptions is all it takes: + * one to be told when the configuration changes, one to answer when + * someone asks for the state. Nothing in the CLI knows about this + * daemon; it picks the model up from sysrepo on its own. + * + * Runs in the foreground, as finit expects, which handles supervision + * and restart. */ #include +#include #include +#include #include #include -#define CONFIG_FILE "/etc/hello-world.conf" +#include +#include + +#define MODULE "example-hello" +#define XPATH_HELLO "/example-hello:hello" +#define XPATH_AUDIENCE XPATH_HELLO "/audience" +#define XPATH_GREETINGS XPATH_HELLO "/greetings" #define INTERVAL 60 static volatile sig_atomic_t running = 1; +static char audience[64] = "world"; +static uint64_t greetings; static void sig_exit(int signo) { @@ -19,38 +40,117 @@ static void sig_exit(int signo) } /* - * The configuration file is shipped by the rootfs overlay, see - * board/common/rootfs/etc/ in the spin. A real daemon would read its - * settings here; this example only verifies the file is in place. + * Read the audience from the running datastore. The leaf has a default + * in the YANG model, so this returns "world" on a system that has never + * been configured -- no fallback needed here. */ -static int load_config(void) +static void read_audience(sr_session_ctx_t *sess) { - FILE *fp; + sr_val_t *val = NULL; + int rc; - fp = fopen(CONFIG_FILE, "r"); - if (!fp) { - syslog(LOG_WARNING, "missing %s, using defaults", CONFIG_FILE); - return -1; + rc = sr_get_item(sess, XPATH_AUDIENCE, 0, &val); + if (rc || !val) { + syslog(LOG_WARNING, "cannot read %s (%s), still greeting '%s'", + XPATH_AUDIENCE, sr_strerror(rc), audience); + return; } - fclose(fp); - return 0; + snprintf(audience, sizeof(audience), "%s", val->data.string_val); + sr_free_val(val); + + syslog(LOG_NOTICE, "greeting '%s' from now on", audience); +} + +/* + * Called once a 'set hello audience ...' has been committed, i.e., after + * 'leave' in the CLI or a NETCONF . + */ +static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module, + const char *xpath, sr_event_t event, uint32_t request_id, void *priv) +{ + if (event == SR_EV_DONE) + read_audience(priv); + + return SR_ERR_OK; +} + +/* + * Called when someone reads operational data, e.g., 'show hello'. We + * hand back a data tree with the counter in it; sysrepo merges it into + * whatever the caller asked for. + */ +static int oper_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module, + const char *path, const char *request_xpath, uint32_t request_id, + struct lyd_node **parent, void *priv) +{ + const struct ly_ctx *ctx; + sr_conn_ctx_t *conn; + char buf[32]; + + conn = sr_session_get_connection(session); + ctx = sr_acquire_context(conn); + + snprintf(buf, sizeof(buf), "%llu", (unsigned long long)greetings); + if (!*parent) + lyd_new_path(NULL, ctx, XPATH_HELLO, NULL, 0, parent); + lyd_new_path(*parent, NULL, XPATH_GREETINGS, buf, 0, NULL); + + sr_release_context(conn); + + return SR_ERR_OK; } int main(void) { + sr_subscription_ctx_t *sub = NULL; + sr_session_ctx_t *sess; + sr_conn_ctx_t *conn; + int rc; + signal(SIGTERM, sig_exit); signal(SIGINT, sig_exit); openlog("hello-world", LOG_PID, LOG_DAEMON); - load_config(); + + rc = sr_connect(0, &conn); + if (rc) { + syslog(LOG_ERR, "cannot connect to sysrepo: %s", sr_strerror(rc)); + return 1; + } + + rc = sr_session_start(conn, SR_DS_RUNNING, &sess); + if (rc) { + syslog(LOG_ERR, "cannot start sysrepo session: %s", sr_strerror(rc)); + return 1; + } + + read_audience(sess); + + /* Tell me when the configuration changes ... */ + rc = sr_module_change_subscribe(sess, MODULE, XPATH_HELLO, change_cb, sess, + 0, SR_SUBSCR_DONE_ONLY, &sub); + if (rc) { + syslog(LOG_ERR, "cannot subscribe to %s: %s", XPATH_HELLO, sr_strerror(rc)); + return 1; + } + + /* ... and ask me when someone wants the state. */ + rc = sr_oper_get_subscribe(sess, MODULE, XPATH_GREETINGS, oper_cb, NULL, 0, &sub); + if (rc) { + syslog(LOG_ERR, "cannot subscribe to %s: %s", XPATH_GREETINGS, sr_strerror(rc)); + return 1; + } while (running) { - syslog(LOG_INFO, "Hello World!"); + syslog(LOG_INFO, "Hello %s!", audience); + greetings++; sleep(INTERVAL); } syslog(LOG_INFO, "exiting"); + sr_unsubscribe(sub); + sr_disconnect(conn); closelog(); return 0; From ba3bcbe90700c993f636c44e3d35476633409554 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 26 Aug 2026 14:49:20 +0200 Subject: [PATCH 10/19] Add 'show hello' to the CLI klish merges the XML files in /etc/klish, so the overlay can extend Infix's show command rather than replace it. This is the only CLI code the model needs. Configure mode is generated from whatever sysrepo has loaded, so 'set hello audience ...' already works without it. --- board/common/rootfs/etc/klish/hello.xml | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 board/common/rootfs/etc/klish/hello.xml diff --git a/board/common/rootfs/etc/klish/hello.xml b/board/common/rootfs/etc/klish/hello.xml new file mode 100644 index 0000000..68a07f9 --- /dev/null +++ b/board/common/rootfs/etc/klish/hello.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + copy operational -x /example-hello:hello | jq -r '.["example-hello:hello"] + | "audience: \(.audience)", "greetings: \(.greetings)"' + + + + + + + From 622c9355928558d0304888d6b9efb31d5f41551c Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 26 Aug 2026 14:49:20 +0200 Subject: [PATCH 11/19] Test the model end to end Sets the audience over NETCONF and checks the daemon acts on it, and that the greeting counter comes back as operational data. --- test/case/hello-world/test.py | 49 +++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/test/case/hello-world/test.py b/test/case/hello-world/test.py index 2064551..57f4d29 100755 --- a/test/case/hello-world/test.py +++ b/test/case/hello-world/test.py @@ -1,28 +1,61 @@ #!/usr/bin/env python3 """Hello World daemon -Verify the pieces this spin adds on top of Infix: the hello-world -package is installed, finit supervises it, its configuration file comes -from the rootfs overlay, and it greets us in the system log. +Verify what this spin adds on top of Infix: the hello-world daemon runs +under finit, takes its configuration from the example-hello YANG model +this repo ships, and reports state back through the same model. + +Nothing here is specific to NETCONF -- the CLI and RESTCONF reach the +very same data, because they are all generated from the model. """ import infamy +NEW = "there" + with infamy.Test() as test: with test.step("Set up topology and attach to target DUT"): env = infamy.Env() + target = env.attach("target", "mgmt") tgtssh = env.attach("target", "mgmt", "ssh") - with test.step("Verify configuration file from the rootfs overlay"): - tgtssh.runsh("test -f /etc/hello-world.conf", check=True) - with test.step("Verify finit supervises the hello-world service"): status = tgtssh.runsh("initctl status hello-world", check=True).stdout print(status) if "running" not in status: test.fail() - with test.step("Verify the greeting reached the system log"): - tgtssh.runsh("sudo grep -q 'Hello World!' /var/log/syslog", check=True) + with test.step("Verify the daemon greets the default audience"): + # Nothing has been configured, so the daemon is running on the + # default from the YANG model. + infamy.until(lambda: tgtssh.runsh("sudo grep -q 'Hello world!' /var/log/syslog", + check=False).returncode == 0) + + with test.step("Verify the greeting counter is reported as operational data"): + oper = target.get_data("/example-hello:hello") + print(f"greetings: {oper['hello']['greetings']}") + if int(oper["hello"]["greetings"]) < 1: + test.fail() + + with test.step(f"Set audience to '{NEW}'"): + target.put_config_dicts({ + "example-hello": { + "hello": { + "audience": NEW, + } + } + }) + + with test.step(f"Verify '{NEW}' is in the running configuration"): + running = target.get_config_dict("/example-hello:hello") + if running["hello"]["audience"] != NEW: + test.fail() + + with test.step(f"Verify the daemon starts greeting '{NEW}'"): + # The daemon greets once a minute, so give it a full cycle to + # pick the new audience up. + infamy.until(lambda: tgtssh.runsh(f"sudo grep -q 'Hello {NEW}!' /var/log/syslog", + check=False).returncode == 0, + attempts=90, interval=1) test.succeed() From be834d02e015fe7a95f17d8f92292495acaff4e4 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 26 Aug 2026 14:49:20 +0200 Subject: [PATCH 12/19] Document the YANG model in the README Walks through what the model buys: the CLI, NETCONF, and RESTCONF all follow from it, and only 'show hello' needed writing. --- README.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a0f29d0..de3a951 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ on top: `config-snippets/` * **Packages** — an example `hello-world` daemon, built from source in this repo and supervised by finit (`package/`, `src/`) - * **Files** — a rootfs overlay for configuration and service settings + * **Models** — a YANG model for that daemon, which is all it takes to + get it into the CLI, NETCONF, and RESTCONF (`yang/`) + * **Files** — a rootfs overlay for service settings and a CLI snippet (`board/common/rootfs/`) * **Tests** — a regression test for the daemon, run in QEMU by Infix's test harness (`test/`) @@ -65,6 +67,38 @@ example-os:~$ sudo grep hello-world /var/log/syslog That's it. Have a look around and make it your own. +## A Model of Your Own + +The daemon is configured the way everything in Infix is: with YANG. The +model lives in `yang/example-hello@2026-08-26.yang` and is a container +with two leaves — `audience`, which you set, and `greetings`, which the +daemon reports. It is loaded into sysrepo when the image is built, see +`package/hello-world/hello-world.mk`. + +That is the entire integration. There is no CLI code for it, because +configure mode is generated from whatever models sysrepo has loaded: + +``` +example-os:~$ configure +example-os:/config/> set hello audience there +example-os:/config/> leave +example-os:~$ show hello +audience: there +greetings: 4 +``` + +Tab completion, help texts, validation, `show`, `diff`, saving to +startup-config — all of it follows from the model. The same data is +reachable over NETCONF and RESTCONF, again for free. + +Only the last command needed writing: `show` in operational mode is an +ordinary CLI command, so the spin drops a klish snippet into +`board/common/rootfs/etc/klish/hello.xml` to add it. + +On the daemon side, two sysrepo subscriptions are all that is needed — +one to hear about `audience` changing, one to answer for `greetings`. +See `src/hello-world/hello-world.c`, it is about thirty lines of it. + ## Test Infix comes with a test harness that boots the image in QEMU and pokes @@ -97,5 +131,7 @@ images. ## Learn More * [Infix documentation](https://kernelkit.org/) + * [RFC 7950](https://www.rfc-editor.org/rfc/rfc7950), the YANG 1.1 + language * [Buildroot br2-external manual](https://buildroot.org/downloads/manual/manual.html#outside-br-custom) * [finit](https://github.com/troglobit/finit) From c1f7710807e4d734da071dd38f8ea94f972c87a8 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Tue, 22 Sep 2026 08:19:57 +0200 Subject: [PATCH 13/19] Ship a starting configuration for the hello audience 'show hello' reported nothing for audience on a factory fresh unit. The default does reach the daemon, it reads 'world' either way, but a leaf still holding its default is left out when the datastore is serialized, so it never makes it to the output. Set it for real instead. Infix collates the factory configuration from /usr/share/confd/factory.d, and boots from test.d under the test harness, so drop a snippet in the former and symlink it across, the way Infix shares its own. --- README.md | 14 ++++++++++++-- .../rootfs/usr/share/confd/factory.d/50-hello.json | 5 +++++ .../rootfs/usr/share/confd/test.d/50-hello.json | 1 + test/case/hello-world/test.py | 9 +++++++-- yang/example-hello@2026-08-26.yang | 10 ++++++++-- 5 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 board/common/rootfs/usr/share/confd/factory.d/50-hello.json create mode 120000 board/common/rootfs/usr/share/confd/test.d/50-hello.json diff --git a/README.md b/README.md index de3a951..65c4f76 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ on top: this repo and supervised by finit (`package/`, `src/`) * **Models** — a YANG model for that daemon, which is all it takes to get it into the CLI, NETCONF, and RESTCONF (`yang/`) - * **Files** — a rootfs overlay for service settings and a CLI snippet - (`board/common/rootfs/`) + * **Files** — a rootfs overlay for service settings, a CLI snippet, and + a factory default (`board/common/rootfs/`) * **Tests** — a regression test for the daemon, run in QEMU by Infix's test harness (`test/`) @@ -95,6 +95,16 @@ Only the last command needed writing: `show` in operational mode is an ordinary CLI command, so the spin drops a klish snippet into `board/common/rootfs/etc/klish/hello.xml` to add it. +A `default` in the model is not the same as a starting configuration. +The daemon reads `world` whether or not anything is set, but a leaf that +still holds its default is left out when the datastore is serialized, so +`show hello` prints nothing for it. To make it visible, ship it as real +configuration: Infix collates the factory config from +`/usr/share/confd/factory.d/`, so the overlay drops a `50-hello.json` +there, and symlinks it into `test.d/` beside it, which is where the +image boots from under the test harness. Anything a spin wants in a +factory fresh configuration goes the same way. + On the daemon side, two sysrepo subscriptions are all that is needed — one to hear about `audience` changing, one to answer for `greetings`. See `src/hello-world/hello-world.c`, it is about thirty lines of it. diff --git a/board/common/rootfs/usr/share/confd/factory.d/50-hello.json b/board/common/rootfs/usr/share/confd/factory.d/50-hello.json new file mode 100644 index 0000000..e3b00c1 --- /dev/null +++ b/board/common/rootfs/usr/share/confd/factory.d/50-hello.json @@ -0,0 +1,5 @@ +{ + "example-hello:hello": { + "audience": "world" + } +} diff --git a/board/common/rootfs/usr/share/confd/test.d/50-hello.json b/board/common/rootfs/usr/share/confd/test.d/50-hello.json new file mode 120000 index 0000000..b9f2972 --- /dev/null +++ b/board/common/rootfs/usr/share/confd/test.d/50-hello.json @@ -0,0 +1 @@ +../factory.d/50-hello.json \ No newline at end of file diff --git a/test/case/hello-world/test.py b/test/case/hello-world/test.py index 57f4d29..b8d9423 100755 --- a/test/case/hello-world/test.py +++ b/test/case/hello-world/test.py @@ -26,8 +26,13 @@ test.fail() with test.step("Verify the daemon greets the default audience"): - # Nothing has been configured, so the daemon is running on the - # default from the YANG model. + # Nothing has been configured, so this is the factory default + # the overlay ships, see board/common/rootfs/usr/share/confd/ + running = target.get_config_dict("/example-hello:hello") + print(running) + if running["hello"]["audience"] != "world": + test.fail() + infamy.until(lambda: tgtssh.runsh("sudo grep -q 'Hello world!' /var/log/syslog", check=False).returncode == 0) diff --git a/yang/example-hello@2026-08-26.yang b/yang/example-hello@2026-08-26.yang index 8e4c394..8331969 100644 --- a/yang/example-hello@2026-08-26.yang +++ b/yang/example-hello@2026-08-26.yang @@ -34,8 +34,14 @@ module example-hello { default "world"; description "Who to greet. The daemon logs 'Hello !' to the system - log once a minute. Being a leaf with a default, it needs no - entry in the startup configuration to have a value."; + log once a minute. + + A default is not the same as a starting configuration. The + daemon reads 'world' here whether or not anything is set, but a + leaf still holding its default is left out when the datastore is + serialized, so 'show hello' would print nothing for it. The + snippets in board/common/rootfs/usr/share/confd/ set it for + real, which is what makes it visible."; } leaf greetings { From 8a1b743a667401a88dedc59fee0d41c0a8540a5d Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Mon, 21 Sep 2026 14:26:18 +0200 Subject: [PATCH 14/19] Upload the test report as its own artifact Reading the report meant downloading the images alongside it, which is a gigabyte of qcow2 to get at a few pages of PDF. --- .github/workflows/build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 327f3eb..1383e98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,11 +71,17 @@ jobs: if: always() run: cat infix/test/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY - # Lands in output/images/, uploaded with the rest below. - name: Test Report x86_64 if: always() run: make test-report + - name: Upload Test Report + if: always() + uses: actions/upload-artifact@v7 + with: + name: test-report + path: output/images/test-report.pdf + # Also on failure, so a retry keeps what did get fetched. - name: Save Downloads if: always() From b4e11318990125a6b79136caa585e4d46059f18b Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Tue, 22 Sep 2026 10:44:07 +0200 Subject: [PATCH 15/19] Stop uploading the whole image directory Artifact uploads were hitting the free storage quota, 87 files a run. Most of it is the same root filesystem over again: rootfs.squashfs, the .itb wrapping it with the kernel, the .pkg wrapping that for RAUC, and the .qcow2 with it written into a disk slot. Keep the qcow2 and OVMF.fd, which is what `make run` boots, and build that on demand rather than on every push. The test report still goes up every run. Both expire after a week instead of the default quarter. --- .github/workflows/build.yml | 12 +++++++++--- README.md | 6 ++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1383e98..b44f3e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,6 +81,7 @@ jobs: with: name: test-report path: output/images/test-report.pdf + retention-days: 7 # Also on failure, so a retry keeps what did get fetched. - name: Save Downloads @@ -97,9 +98,14 @@ jobs: path: .ccache/ key: ccache-${{ github.run_id }}-${{ github.run_attempt }} - - name: Upload Images - if: always() + # Just what `make run` boots, on demand, to stay inside the free + # artifact quota. + - name: Upload Image + if: github.event_name == 'workflow_dispatch' uses: actions/upload-artifact@v7 with: name: example-os-x86_64 - path: output/images/ + path: | + output/images/*.qcow2 + output/images/OVMF.fd + retention-days: 7 diff --git a/README.md b/README.md index 65c4f76..3cdbc73 100644 --- a/README.md +++ b/README.md @@ -135,8 +135,10 @@ make test-report # -> output/images/test-report.pdf ``` The same commands run in CI, on a stock GitHub runner, see -`.github/workflows/build.yml`. The report is uploaded along with the -images. +`.github/workflows/build.yml`. Every run uploads the report; a bootable +image is a few hundred MB, more than a free account's artifact storage +will take on every push, so that one is built on demand with the "Run +workflow" button. ## Learn More From c84380185c451e9560daf53cff296dc25c308963 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Tue, 22 Sep 2026 13:25:14 +0200 Subject: [PATCH 16/19] Attach images to a release on tags Artifacts expire, 90 days at the most, which is no good for keeping a released image around. Release assets have no expiry, so build a release on v* tags with the qcow2, the firmware, and the test report. --- .github/workflows/build.yml | 14 ++++++++++++++ README.md | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b44f3e8..cc676f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,7 @@ name: Build & Test on: push: branches: [main] + tags: ['v*'] pull_request: workflow_dispatch: @@ -10,6 +11,8 @@ jobs: x86_64: name: ExampleOS x86_64 runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Free Disk Space uses: jlumbroso/free-disk-space@main @@ -109,3 +112,14 @@ jobs: output/images/*.qcow2 output/images/OVMF.fd retention-days: 7 + + # Artifacts expire after 90 days at most, release assets never do. + - name: Release + if: startsWith(github.ref, 'refs/tags/') + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "$GITHUB_REF_NAME" --generate-notes \ + output/images/*.qcow2 \ + output/images/OVMF.fd \ + output/images/test-report.pdf diff --git a/README.md b/README.md index 3cdbc73..512e63e 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,8 @@ The same commands run in CI, on a stock GitHub runner, see `.github/workflows/build.yml`. Every run uploads the report; a bootable image is a few hundred MB, more than a free account's artifact storage will take on every push, so that one is built on demand with the "Run -workflow" button. +workflow" button. Push a `v*` tag and both are attached to a GitHub +release instead, where they are kept indefinitely. ## Learn More From 55e700e63285847ca981eea75565daee72c1a307 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 30 Sep 2026 12:54:03 +0000 Subject: [PATCH 17/19] Makefile: forward unknown targets to Infix, not Buildroot Infix already forwards what it does not handle itself to Buildroot, so the catch-all can go through Infix. Any Infix target then works from here, including the new 'make migrate-configs' to bump the factory config to the current confd version. Signed-off-by: Richard Alpe --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 4bf4cfd..7d84026 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ export LOGO := $(CURDIR)/doc/logo.png[top=40%, align=right, pdfwidth=10cm] O ?= $(CURDIR)/output config := $(O)/.config bmake = $(MAKE) -C infix/buildroot O=$(O) $1 +imake = $(MAKE) -C infix O=$(O) $1 all: $(config) infix/buildroot/Makefile @+$(call bmake,$@) @@ -32,16 +33,17 @@ $(config): # Explicit rule, since the catch-all below would find the test/ directory # and think there is nothing to do. test: | infix/buildroot/Makefile - @+$(call bmake,$@) + @+$(call imake,$@) # Render the result of the last `make test` as output/images/test-report.pdf. # Needs asciidoctor-pdf: gem install asciidoctor-pdf test-report: | infix/buildroot/Makefile - @+$(call bmake,$@) + @+$(call imake,$@) -# Any other target (menuconfig, clean, ...) is passed to Buildroot. +# Any other target (menuconfig, clean, migrate-configs, ...) is passed to +# Infix, which handles its own targets and forwards the rest to Buildroot. %: | infix/buildroot/Makefile - @+$(call bmake,$@) + @+$(call imake,$@) infix/buildroot/Makefile: @git submodule update --init --recursive From 21b65ed4067218ffdebd9e0fea101696799ed986 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 30 Sep 2026 13:09:03 +0000 Subject: [PATCH 18/19] README: link to Infix build instructions Drop the list of build dependencies, it is a moving target. Refer to the Developer's Guide for the Infix release the spin is based on. Signed-off-by: Richard Alpe --- README.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 512e63e..9de97dd 100644 --- a/README.md +++ b/README.md @@ -27,19 +27,13 @@ and you have your own OS. ## Get Started -Clone the repository along with its submodules, and install the tools -Buildroot needs: +Install the build dependencies listed in the [Infix Developer's +Guide][building], for the Infix release this spin is based on, then +clone the repository along with its submodules: ```bash git clone --recurse-submodules https://github.com/kernelkit/infix-spin-example.git cd infix-spin-example - -sudo apt install bc binutils build-essential bzip2 cpio \ - diffutils file findutils git gzip \ - libncurses-dev libssl-dev perl patch \ - python3 rsync sed tar unzip wget \ - autopoint bison flex autoconf automake \ - mtools ``` Configure and build. The first build takes a while, since it compiles @@ -148,3 +142,5 @@ release instead, where they are kept indefinitely. language * [Buildroot br2-external manual](https://buildroot.org/downloads/manual/manual.html#outside-br-custom) * [finit](https://github.com/troglobit/finit) + +[building]: https://www.kernelkit.org/infix/26.06/developers-guide/#building From c9d5872500126e3013837ed3123b6d078a96d9b3 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Wed, 14 Oct 2026 16:23:59 +0200 Subject: [PATCH 19/19] Use the new defconfigs-update tool and update configs Signed-off-by: Richard Alpe --- Makefile | 14 ++- config-snippets/common.conf | 29 ----- config-snippets/x86_64.conf | 3 - configs/x86_64_defconfig | 14 +-- configs/x86_64_defconfig.base | 197 ++++++++++++++++++++++++++++++++++ infix | 2 +- 6 files changed, 212 insertions(+), 47 deletions(-) delete mode 100644 config-snippets/common.conf delete mode 100644 config-snippets/x86_64.conf create mode 100644 configs/x86_64_defconfig.base diff --git a/Makefile b/Makefile index 7d84026..596e21c 100644 --- a/Makefile +++ b/Makefile @@ -48,13 +48,11 @@ test-report: | infix/buildroot/Makefile infix/buildroot/Makefile: @git submodule update --init --recursive -# Regenerate configs/ from Infix's defconfigs + our snippets. Run this -# after editing anything in config-snippets/, and commit the result. -defconfigs-generate: infix/buildroot/Makefile - ./infix/utils/generate-defconfig.sh \ - -b infix/configs/x86_64_defconfig \ - -c config-snippets/common.conf \ - -c config-snippets/x86_64.conf \ - -o configs/x86_64_defconfig +# configs/*.base. YES=1 follows Infix on everything +sync = ./infix/utils/update-defconfig.sh $(if $(YES),-y) + +# Regenerate configs/ +defconfigs-update: + @$(sync) infix/configs/x86_64_defconfig configs/x86_64_defconfig .PHONY: all test test-report defconfigs-generate diff --git a/config-snippets/common.conf b/config-snippets/common.conf deleted file mode 100644 index d21efc4..0000000 --- a/config-snippets/common.conf +++ /dev/null @@ -1,29 +0,0 @@ -# Branding -- shows up in /etc/os-release, the boot tagline, the CLI, -# and the generated image file names. Make it yours. -IX_VENDOR="Example Inc." -IX_VENDOR_HOME="https://example.com" -IX_NAME="ExampleOS" -IX_ID="example-os" -IX_TAGLINE="ExampleOS -- an Infix spin" -IX_DESC="Example spin of Infix OS, use as a starting point for your own." -IX_HOME="https://example.com" -IX_DOC="https://example.com/docs" -IX_SUPPORT="mailto:support@example.com" -BR2_TARGET_GENERIC_HOSTNAME="example-os" - -# Keep the RAUC compatible string of vanilla Infix so upgrades can be -# exchanged with stock Infix images of the same architecture. Set your -# own (e.g. "${IX_ID}-${BR2_ARCH}") to lock upgrades to your spin. -IX_COMPATIBLE="infix-${BR2_ARCH}" - -# Version the OS from this repo's git tags: Infix's post-build.sh runs -# `git describe` here for the BUILD_ID in /etc/os-release. -IX_OEM_PATH="${BR2_EXTERNAL_EXAMPLE_PATH}" - -# Keep downloads and compiler cache in the spin repo (survives -# `make clean` / output/ wipes). -BR2_DL_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/dl" -BR2_CCACHE_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/.ccache" - -# Packages from this spin (see package/) -BR2_PACKAGE_HELLO_WORLD=y diff --git a/config-snippets/x86_64.conf b/config-snippets/x86_64.conf deleted file mode 100644 index 74c6cab..0000000 --- a/config-snippets/x86_64.conf +++ /dev/null @@ -1,3 +0,0 @@ -# Stack our rootfs overlay on top of Infix's. Buildroot applies the -# directories in order, so files here override same-named Infix files. -BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/rootfs ${BR2_EXTERNAL_EXAMPLE_PATH}/board/common/rootfs" diff --git a/configs/x86_64_defconfig b/configs/x86_64_defconfig index 5a61a8f..c33946a 100644 --- a/configs/x86_64_defconfig +++ b/configs/x86_64_defconfig @@ -2,8 +2,8 @@ BR2_x86_64=y BR2_x86_corei7=y BR2_TOOLCHAIN_EXTERNAL=y BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY=y -BR2_DL_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/dl" BR2_WGET="wget -nd -t 5 --waitretry=30 --retry-on-http-error=429,500,502,503,504 --connect-timeout=10" +BR2_DL_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/dl" BR2_CCACHE=y BR2_CCACHE_DIR="${BR2_EXTERNAL_EXAMPLE_PATH}/.ccache" BR2_ENABLE_DEBUG=y @@ -16,7 +16,6 @@ BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs" BR2_ROOTFS_MERGED_USR=y # BR2_TARGET_ENABLE_ROOT_LOGIN is not set -BR2_SYSTEM_BIN_SH_BASH=y BR2_TARGET_GENERIC_GETTY_PORT="@console" BR2_TARGET_GENERIC_GETTY_TERM="xterm" BR2_SYSTEM_DHCP="eth0" @@ -27,7 +26,7 @@ BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNA BR2_ROOTFS_POST_BUILD_SCRIPT="board/qemu/x86_64/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh" BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_VERSION=y -BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.44" +BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.56" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/linux_defconfig" BR2_LINUX_KERNEL_INSTALL_TARGET=y @@ -51,13 +50,12 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SUPPORT=y BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y -BR2_PACKAGE_LIBSSH_OPENSSL=y BR2_PACKAGE_LIBSSH2=y BR2_PACKAGE_LIBSSH2_OPENSSL=y BR2_PACKAGE_LIBOPENSSL_BIN=y BR2_PACKAGE_LIBINPUT=y BR2_PACKAGE_LIBCURL_CURL=y -BR2_PACKAGE_NETOPEER2_CLI=y +BR2_PACKAGE_LIBNETCONF2_SSH_SUBSYSTEM=y BR2_PACKAGE_NSS_MDNS=y BR2_PACKAGE_SYSREPO_GROUP="sysrepo" BR2_PACKAGE_LINUX_PAM=y @@ -83,6 +81,8 @@ BR2_PACKAGE_MTR=y BR2_PACKAGE_NETCALC=y BR2_PACKAGE_NETCAT_OPENBSD=y BR2_PACKAGE_NETSNMP=y +# BR2_PACKAGE_NETSNMP_ENABLE_MIBS is not set +BR2_PACKAGE_NETSNMP_WITH_MIB_MODULES="host if-mib ucd-snmp/loadave ucd-snmp/memory ucd-snmp/disk_hw agentx" BR2_PACKAGE_NGINX=y BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y @@ -93,6 +93,7 @@ BR2_PACKAGE_NMAP_NPING=y BR2_PACKAGE_ODHCP6C=y BR2_PACKAGE_OPENRESOLV=y BR2_PACKAGE_OPENSSH=y +BR2_PACKAGE_PPPD=y BR2_PACKAGE_SOCAT=y BR2_PACKAGE_TCPDUMP=y BR2_PACKAGE_TRACEROUTE=y @@ -115,7 +116,6 @@ BR2_PACKAGE_RAUC_NETWORK=y BR2_PACKAGE_RAUC_JSON=y BR2_PACKAGE_SYSKLOGD=y BR2_PACKAGE_SYSKLOGD_LOGGER=y -BR2_PACKAGE_WATCHDOGD=y BR2_PACKAGE_WATCHDOGD_GENERIC=y BR2_PACKAGE_WATCHDOGD_LOADAVG=y BR2_PACKAGE_WATCHDOGD_FILENR=y @@ -154,6 +154,7 @@ IX_SUPPORT="mailto:support@example.com" IX_OEM_PATH="${BR2_EXTERNAL_EXAMPLE_PATH}" BR2_PACKAGE_FEATURE_GPS=y BR2_PACKAGE_FEATURE_WIFI=y +BR2_PACKAGE_FEATURE_WIFI_HWSIM=y BR2_PACKAGE_FEATURE_WIFI_MEDIATEK=y BR2_PACKAGE_FEATURE_WIFI_REALTEK=y BR2_PACKAGE_FEATURE_WIFI_QUALCOMM=y @@ -164,6 +165,7 @@ BR2_PACKAGE_CURIOS_HTTPD=y BR2_PACKAGE_CURIOS_NFTABLES=y BR2_PACKAGE_GENCERT=y BR2_PACKAGE_STATD=y +BR2_PACKAGE_SUPPORT_ENCRYPT=y BR2_PACKAGE_FACTORY=y BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y BR2_PACKAGE_FINIT_PLUGIN_HOOK_SCRIPTS=y diff --git a/configs/x86_64_defconfig.base b/configs/x86_64_defconfig.base new file mode 100644 index 0000000..2d73704 --- /dev/null +++ b/configs/x86_64_defconfig.base @@ -0,0 +1,197 @@ +BR2_x86_64=y +BR2_x86_corei7=y +BR2_TOOLCHAIN_EXTERNAL=y +BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY=y +BR2_DL_DIR="${BR2_EXTERNAL_INFIX_PATH}/dl" +BR2_CCACHE=y +BR2_CCACHE_DIR="${BR2_EXTERNAL_INFIX_PATH}/.ccache" +BR2_ENABLE_DEBUG=y +BR2_GLOBAL_PATCH_DIR="${BR2_EXTERNAL_INFIX_PATH}/patches" +BR2_DOWNLOAD_FORCE_CHECK_HASHES=y +BR2_TARGET_GENERIC_HOSTNAME="infix" +BR2_TARGET_GENERIC_ISSUE="Infix by KernelKit" +BR2_INIT_FINIT=y +BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y +BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs" +BR2_ROOTFS_MERGED_USR=y +# BR2_TARGET_ENABLE_ROOT_LOGIN is not set +BR2_TARGET_GENERIC_GETTY_PORT="@console" +BR2_TARGET_GENERIC_GETTY_TERM="xterm" +BR2_SYSTEM_DHCP="eth0" +BR2_ENABLE_LOCALE_WHITELIST="C en_US en_CA C.UTF-8" +BR2_GENERATE_LOCALE="en_US en_CA C.UTF-8" +BR2_TARGET_TZ_INFO=y +BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/rootfs" +BR2_ROOTFS_POST_BUILD_SCRIPT="board/qemu/x86_64/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh" +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_VERSION=y +BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.56" +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/linux_defconfig" +BR2_LINUX_KERNEL_INSTALL_TARGET=y +BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y +BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig" +BR2_PACKAGE_STRACE=y +BR2_PACKAGE_STRESS_NG=y +BR2_PACKAGE_JQ=y +BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y +BR2_PACKAGE_DBUS_CXX=y +BR2_PACKAGE_DBUS_GLIB=y +BR2_PACKAGE_EUDEV_RULES_GEN=y +# BR2_PACKAGE_EUDEV_ENABLE_HWDB is not set +BR2_PACKAGE_GPTFDISK=y +BR2_PACKAGE_GPTFDISK_SGDISK=y +BR2_PACKAGE_RNG_TOOLS=y +BR2_PACKAGE_UBOOT_TOOLS=y +BR2_PACKAGE_UBOOT_TOOLS_FIT_SUPPORT=y +BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y +BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y +BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y +BR2_PACKAGE_LIBNETCONF2_SSH_SUBSYSTEM=y +BR2_PACKAGE_LIBSSH2=y +BR2_PACKAGE_LIBSSH2_OPENSSL=y +BR2_PACKAGE_LIBOPENSSL_BIN=y +BR2_PACKAGE_LIBINPUT=y +BR2_PACKAGE_LIBCURL_CURL=y +BR2_PACKAGE_NSS_MDNS=y +BR2_PACKAGE_SYSREPO_GROUP="sysrepo" +BR2_PACKAGE_LINUX_PAM=y +BR2_PACKAGE_LIBPAM_RADIUS_AUTH=y +BR2_PACKAGE_ONIGURUMA=y +BR2_PACKAGE_AVAHI_DAEMON=y +BR2_PACKAGE_AVAHI_DEFAULT_SERVICES=y +BR2_PACKAGE_CHRONY=y +BR2_PACKAGE_CONNTRACK_TOOLS=y +BR2_PACKAGE_DNSMASQ=y +BR2_PACKAGE_ETHTOOL=y +BR2_PACKAGE_FPING=y +BR2_PACKAGE_FRR=y +# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set +BR2_PACKAGE_IPERF3=y +BR2_PACKAGE_IPROUTE2=y +BR2_PACKAGE_IPTABLES_NFTABLES=y +BR2_PACKAGE_IPUTILS=y +BR2_PACKAGE_LINUXPTP=y +BR2_PACKAGE_LLDPD=y +BR2_PACKAGE_MSTPD=y +BR2_PACKAGE_MTR=y +BR2_PACKAGE_NETCALC=y +BR2_PACKAGE_NETCAT_OPENBSD=y +BR2_PACKAGE_NETSNMP=y +BR2_PACKAGE_NETSNMP_WITH_MIB_MODULES="host if-mib ucd-snmp/loadave ucd-snmp/memory ucd-snmp/disk_hw agentx" +# BR2_PACKAGE_NETSNMP_ENABLE_MIBS is not set +BR2_PACKAGE_NGINX=y +BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y +BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y +BR2_PACKAGE_NMAP=y +BR2_PACKAGE_NMAP_NCAT=y +BR2_PACKAGE_NMAP_NMAP=y +BR2_PACKAGE_NMAP_NPING=y +BR2_PACKAGE_ODHCP6C=y +BR2_PACKAGE_OPENRESOLV=y +BR2_PACKAGE_OPENSSH=y +BR2_PACKAGE_PPPD=y +BR2_PACKAGE_SOCAT=y +BR2_PACKAGE_TCPDUMP=y +BR2_PACKAGE_TRACEROUTE=y +BR2_PACKAGE_ULOGD=y +BR2_PACKAGE_WHOIS=y +BR2_PACKAGE_WIREGUARD_TOOLS=y +BR2_PACKAGE_BASH=y +BR2_PACKAGE_BASH_COMPLETION=y +BR2_PACKAGE_NEOFETCH=y +BR2_PACKAGE_SUDO=y +BR2_PACKAGE_TTYD=y +BR2_PACKAGE_GETENT=y +BR2_PACKAGE_HTOP=y +BR2_PACKAGE_IRQBALANCE=y +BR2_PACKAGE_KMOD_TOOLS=y +BR2_PACKAGE_PWGEN=y +BR2_PACKAGE_RAUC=y +BR2_PACKAGE_RAUC_DBUS=y +BR2_PACKAGE_RAUC_GPT=y +BR2_PACKAGE_RAUC_NETWORK=y +BR2_PACKAGE_RAUC_JSON=y +BR2_PACKAGE_SYSKLOGD=y +BR2_PACKAGE_SYSKLOGD_LOGGER=y +BR2_PACKAGE_WATCHDOGD=y +BR2_PACKAGE_WATCHDOGD_GENERIC=y +BR2_PACKAGE_WATCHDOGD_LOADAVG=y +BR2_PACKAGE_WATCHDOGD_FILENR=y +BR2_PACKAGE_WATCHDOGD_MEMINFO=y +BR2_PACKAGE_WATCHDOGD_FSMON=y +BR2_PACKAGE_WATCHDOGD_TEMPMON=y +BR2_PACKAGE_LESS=y +BR2_PACKAGE_MG=y +BR2_PACKAGE_NANO=y +# BR2_TARGET_ROOTFS_TAR is not set +BR2_TARGET_EDK2=y +BR2_TARGET_GRUB2=y +BR2_TARGET_GRUB2_X86_64_EFI=y +BR2_TARGET_GRUB2_BUILTIN_MODULES_EFI="boot linux ext2 squash4 part_gpt normal efi_gop configfile loadenv test echo reboot net efinet tftp loopback cat search" +BR2_TARGET_GRUB2_BUILTIN_CONFIG_EFI="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/grub-embed.cfg" +BR2_TARGET_GRUB2_INSTALL_TOOLS=y +BR2_PACKAGE_HOST_DOSFSTOOLS=y +BR2_PACKAGE_HOST_E2FSPROGS=y +BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y +BR2_PACKAGE_HOST_GENEXT2FS=y +BR2_PACKAGE_HOST_GO_BIN=y +BR2_PACKAGE_HOST_MTOOLS=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y +BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y +IX_VENDOR_HOME="https://www.kernelkit.org" +IX_DESC="Infix is an immutable, friendly, and secure operating system that turns any ARM or x86 device into a powerful, manageable network appliance. Deploy on anything from $35 Raspberry Pi boards to enterprise switches as routers, IoT gateways, or edge devices. Infix models Linux networking features using YANG so you can manage your devices using NETCONF/RESTCONF APIs and focus on your business logic running in isolated containers." +IX_HOME="https://github.com/kernelkit/infix/" +IX_DOC="https://www.kernelkit.org/infix/" +IX_SUPPORT="mailto:kernelkit@googlegroups.com" +BR2_PACKAGE_FEATURE_GPS=y +BR2_PACKAGE_FEATURE_WIFI=y +BR2_PACKAGE_FEATURE_WIFI_HWSIM=y +BR2_PACKAGE_FEATURE_WIFI_MEDIATEK=y +BR2_PACKAGE_FEATURE_WIFI_QUALCOMM=y +BR2_PACKAGE_FEATURE_WIFI_REALTEK=y +BR2_PACKAGE_CONFD=y +BR2_PACKAGE_NETD=y +BR2_PACKAGE_CONFD_TEST_MODE=y +BR2_PACKAGE_CURIOS_HTTPD=y +BR2_PACKAGE_CURIOS_NFTABLES=y +BR2_PACKAGE_GENCERT=y +BR2_PACKAGE_STATD=y +BR2_PACKAGE_SUPPORT_ENCRYPT=y +BR2_PACKAGE_FACTORY=y +BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y +BR2_PACKAGE_FINIT_PLUGIN_HOOK_SCRIPTS=y +BR2_PACKAGE_FINIT_PLUGIN_MODULES_LOAD=y +BR2_PACKAGE_FINIT_PLUGIN_RTC=y +BR2_PACKAGE_FINIT_RTC_DATE="2024-11-04 10:54:00" +BR2_PACKAGE_FINIT_RTC_FILE="/var/lib/misc/rtc" +BR2_PACKAGE_FINIT_PLUGIN_TTY=y +BR2_PACKAGE_FINIT_PLUGIN_URANDOM=y +BR2_PACKAGE_FIREWALL=y +BR2_PACKAGE_IITO=y +BR2_PACKAGE_KEYACK=y +BR2_PACKAGE_KLISH_PLUGIN_INFIX=y +BR2_PACKAGE_LOWDOWN=y +BR2_PACKAGE_MCD=y +BR2_PACKAGE_MDNS_ALIAS=y +BR2_PACKAGE_NETBROWSE=y +BR2_PACKAGE_ONIEPROM=y +BR2_PACKAGE_PODMAN=y +BR2_PACKAGE_PODMAN_DRIVER_BTRFS=y +BR2_PACKAGE_PODMAN_DRIVER_DEVICEMAPPER=y +BR2_PACKAGE_PODMAN_DRIVER_VFS=y +BR2_PACKAGE_TETRIS=y +BR2_PACKAGE_ROUSETTE=y +# BR2_PACKAGE_LANDING is not set +BR2_PACKAGE_WEBUI=y +BR2_PACKAGE_RAUC_INSTALLATION_STATUS=y +BR2_PACKAGE_HOST_PYTHON_YANGDOC=y +BR2_PACKAGE_PCIUTILS=y +IX_IMAGE_ITB_AUX=y +IX_IMAGE_ITB_QCOW=y +IX_IMAGE_ITB_RAUC=y +IX_IMAGE_README=y +IX_TRUSTED_KEYS=y +IX_TRUSTED_KEYS_DEVELOPMENT=y diff --git a/infix b/infix index 279fc86..6eb2e07 160000 --- a/infix +++ b/infix @@ -1 +1 @@ -Subproject commit 279fc868a11228d5b932d54c74744ca0b11a06fc +Subproject commit 6eb2e079fdc292d102cfb88ccb3fd7e4c11df052