From c37f7314a742a31b69ec7a9d5e46b33947dcfa5c Mon Sep 17 00:00:00 2001 From: Tomaz Zaman Date: Mon, 10 Aug 2026 17:13:11 +0200 Subject: [PATCH 14/72] mono: automated update pipeline scripts/mono-update.sh (cron-safe): rebases mono onto the newest stable tag, rebuilds from the committed seed, tags mono-vX.Y.Z before building so the image bakes its own release identity, stages artifacts + patch stack + latest.json under releases/, and publishes via rsync only when MONO_PUBLISH_DEST/MONO_PUBLISH_URL come from the environment - no infrastructure knowledge is committed. Refuses to run on a dirty tree or wrong branch; aborts cleanly on rebase conflict. mono-update-check (on device): daily crond check of https://openwrt.mono.si/latest.json against /etc/mono_release. notify mode (default) logs and drops a state file; auto mode downloads, verifies sha256 and applies sysupgrade unattended. Fleet-friendly randomized check time. Co-Authored-By: Claude Fable 5 --- package/mono/mono-update-check/Makefile | 53 +++++++++++ .../files/90-mono-update-cron | 10 +++ .../mono-update-check/files/mono-update-check | 47 ++++++++++ .../files/mono-update.config | 6 ++ scripts/mono-update.sh | 90 +++++++++++++++++++ target/linux/layerscape/image/armv8_64b.mk | 2 +- 6 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 package/mono/mono-update-check/Makefile create mode 100644 package/mono/mono-update-check/files/90-mono-update-cron create mode 100644 package/mono/mono-update-check/files/mono-update-check create mode 100644 package/mono/mono-update-check/files/mono-update.config create mode 100755 scripts/mono-update.sh diff --git a/package/mono/mono-update-check/Makefile b/package/mono/mono-update-check/Makefile new file mode 100644 index 0000000000..aff87ba19b --- /dev/null +++ b/package/mono/mono-update-check/Makefile @@ -0,0 +1,53 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=mono-update-check +PKG_VERSION:=1.0 +PKG_RELEASE:=1 +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/mono-update-check + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Mono Gateway update checker + DEPENDS:=@TARGET_layerscape +endef + +define Package/mono-update-check/description + Daily check of a mono firmware server (latest.json) against the + image's baked release tag. Disabled until uci sets the server URL: + uci set mono-update.check.url='https://.../openwrt' + mode 'notify' (default) logs availability; mode 'auto' downloads, + verifies sha256 and applies sysupgrade unattended. +endef + +define Build/Compile +endef + +# Bake the release identity this image was built as. The mono-update.sh +# release flow tags the tree before building; developer builds get 'dev' +# and the checker treats any difference as an available update. +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + git -C $(TOPDIR) describe --tags --match 'mono-v*' --abbrev=0 \ + > $(PKG_BUILD_DIR)/mono_release 2>/dev/null || \ + echo "dev" > $(PKG_BUILD_DIR)/mono_release +endef + +define Package/mono-update-check/conffiles +/etc/config/mono-update +endef + +define Package/mono-update-check/install + $(INSTALL_DIR) $(1)/etc + $(INSTALL_DATA) $(PKG_BUILD_DIR)/mono_release $(1)/etc/mono_release + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/mono-update.config $(1)/etc/config/mono-update + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/mono-update-check $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files/90-mono-update-cron $(1)/etc/uci-defaults/ +endef + +$(eval $(call BuildPackage,mono-update-check)) diff --git a/package/mono/mono-update-check/files/90-mono-update-cron b/package/mono/mono-update-check/files/90-mono-update-cron new file mode 100644 index 0000000000..6c9e2b875b --- /dev/null +++ b/package/mono/mono-update-check/files/90-mono-update-cron @@ -0,0 +1,10 @@ +#!/bin/sh +# Daily update check at a fixed-per-device random minute/hour so a fleet +# doesn't stampede the server at midnight. +grep -q mono-update-check /etc/crontabs/root 2>/dev/null && exit 0 +M=$(( $(hexdump -n2 -e '"%u"' /dev/urandom) % 60 )) +H=$(( $(hexdump -n2 -e '"%u"' /dev/urandom) % 24 )) +mkdir -p /etc/crontabs +echo "$M $H * * * /usr/sbin/mono-update-check" >> /etc/crontabs/root +/etc/init.d/cron restart 2>/dev/null +exit 0 diff --git a/package/mono/mono-update-check/files/mono-update-check b/package/mono/mono-update-check/files/mono-update-check new file mode 100644 index 0000000000..798e9d084a --- /dev/null +++ b/package/mono/mono-update-check/files/mono-update-check @@ -0,0 +1,47 @@ +#!/bin/sh +# Compare the image's baked release tag against the server's latest.json. +# mode=notify: log and drop a state file. mode=auto: download, verify +# sha256, sysupgrade (config is preserved by the platform upgrade flow). +. /lib/functions.sh + +URL=$(uci -q get mono-update.check.url) +MODE=$(uci -q get mono-update.check.mode) +STATE=/tmp/mono-update-available + +[ -n "$URL" ] || exit 0 + +CURRENT=$(cat /etc/mono_release 2>/dev/null || echo dev) +JSON=$(wget -q -T 30 -O - "$URL/latest.json") || { + logger -t mono-update "cannot fetch $URL/latest.json" + exit 0 +} + +TAG=$(echo "$JSON" | jsonfilter -e '@.tag') +SYSUPGRADE_URL=$(echo "$JSON" | jsonfilter -e '@.sysupgrade') +SHA=$(echo "$JSON" | jsonfilter -e '@.sha256') + +[ -n "$TAG" ] || exit 0 +if [ "$TAG" = "$CURRENT" ]; then + rm -f "$STATE" + exit 0 +fi + +logger -t mono-update "update available: $TAG (running $CURRENT)" +echo "$TAG $SYSUPGRADE_URL" > "$STATE" + +[ "$MODE" = "auto" ] || exit 0 + +FILE=/tmp/mono-sysupgrade.bin +logger -t mono-update "auto mode: downloading $TAG" +wget -q -T 300 -O "$FILE" "$SYSUPGRADE_URL" || { + logger -t mono-update "download failed" + rm -f "$FILE" + exit 1 +} +echo "$SHA $FILE" | sha256sum -c -s || { + logger -t mono-update "sha256 mismatch, refusing to flash" + rm -f "$FILE" + exit 1 +} +logger -t mono-update "applying $TAG via sysupgrade" +exec sysupgrade "$FILE" diff --git a/package/mono/mono-update-check/files/mono-update.config b/package/mono/mono-update-check/files/mono-update.config new file mode 100644 index 0000000000..2728489b1c --- /dev/null +++ b/package/mono/mono-update-check/files/mono-update.config @@ -0,0 +1,6 @@ +config check 'check' + # Server base URL (the directory holding latest.json). + # Empty disables checking. + option url 'https://openwrt.mono.si' + # notify: log availability only. auto: download, verify, sysupgrade. + option mode 'notify' diff --git a/scripts/mono-update.sh b/scripts/mono-update.sh new file mode 100755 index 0000000000..076bc8859d --- /dev/null +++ b/scripts/mono-update.sh @@ -0,0 +1,90 @@ +#!/bin/sh +# Rebase the mono branch onto the newest OpenWrt stable tag, rebuild, +# and stage release artifacts. Designed for cron: quiet no-op when +# already current, hard stop (nonzero, clean tree) on conflicts. +# +# No infrastructure knowledge lives here. Publishing only happens when +# the environment provides: +# MONO_PUBLISH_DEST rsync destination (e.g. host:/srv/openwrt) +# MONO_PUBLISH_URL public base URL written into latest.json +# +# Usage: scripts/mono-update.sh [--dry-run] +set -eu + +cd "$(dirname "$0")/.." +BRANCH=mono +DRY_RUN=${1:-} + +[ "$(git branch --show-current)" = "$BRANCH" ] || { + echo "mono-update: not on branch $BRANCH, refusing" >&2 + exit 1 +} +[ -z "$(git status --porcelain)" ] || { + echo "mono-update: working tree not clean, refusing" >&2 + exit 1 +} + +git fetch --quiet origin 'refs/tags/v*:refs/tags/v*' + +BASE=$(git tag --merged "$BRANCH" 'v[0-9]*' | sort -V | tail -1) +SERIES=${BASE%.*} +LATEST=$(git tag -l "${SERIES}.*" | sort -V | tail -1) + +if [ "$LATEST" = "$BASE" ]; then + echo "mono-update: up to date (base $BASE)" + exit 0 +fi + +echo "mono-update: $BASE -> $LATEST" +[ "$DRY_RUN" = "--dry-run" ] && exit 0 + +if ! git rebase --onto "$LATEST" "$BASE" "$BRANCH"; then + git rebase --abort + echo "mono-update: REBASE CONFLICT rebasing onto $LATEST - resolve manually:" >&2 + echo " git rebase --onto $LATEST $BASE $BRANCH" >&2 + exit 1 +fi + +# Tag before building so the image can bake its own release identity +# (mono-update-check reads it at build time). Dropped again on failure. +RELTAG="mono-$LATEST" +git tag -f "$RELTAG" + +cleanup_fail() { + git tag -d "$RELTAG" >/dev/null 2>&1 || true + echo "mono-update: BUILD FAILED for $RELTAG" >&2 + exit 1 +} + +cp configs/mono_gateway-dk.seed .config +make defconfig || cleanup_fail +make -j"$(nproc)" world || cleanup_fail + +OUT="releases/$RELTAG" +BINDIR=bin/targets/layerscape/armv8_64b +rm -rf "$OUT" +mkdir -p "$OUT" +cp "$BINDIR"/openwrt-layerscape-armv8_64b-mono_gateway-dk-ext4-emmc.img.gz "$OUT/" +cp "$BINDIR"/openwrt-layerscape-armv8_64b-mono_gateway-dk-ext4-sysupgrade.bin "$OUT/" +cp "$BINDIR"/openwrt-layerscape-armv8_64b-mono_gateway-dk.manifest "$OUT/" +git format-patch --quiet -o "$OUT/patches" "$LATEST..$BRANCH" +(cd "$OUT" && sha256sum *.img.gz *.bin > sha256sums) + +SYSUPGRADE_SHA=$(sha256sum "$OUT"/*-sysupgrade.bin | cut -d' ' -f1) +cat > releases/latest.json <