mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-28 09:11:11 -07:00
Setting the release number was a platform change. FORGEFIRM_RELEASE sat in forgefirm-image.bb, the recipe hashes as content of meta-forgefirm, and a change to the content of a layer invalidates every acceptance result. So a version bump threw away the campaign that was meant to authorize that very release, and the number therefore had to be decided before the image the campaign ran on. Nothing said so: the release-flow page went straight from the kas configuration to the artifact and the pipeline, while the gate quietly required the recipe value, the rootfs stamp, the archive's meta-version and the tag to agree. v0.0.1 was cut on a tree whose number happened to be right; the next one would have cost a second campaign to discover the rule. The number moves to forgefirm-release.inc, which carries it and nothing else, and the manifest leaves that file out of the layer content hash exactly as it leaves out the component pin files (FORGEFIRM_MANIFEST_VERSION_SUFFIX, and the same list in scripts/manifest-from-tree.py, which computes the identity on a workstation and must agree byte for byte). release.sh reads the number from the new file. The version is metadata, not platform content, and this only makes the manifest say what it already meant: the version string was already outside the identity hash, and it was the file carrying it that defeated that. Nothing is weakened. release.sh still requires the number to equal the rootfs stamp, the .fw meta-version and the release tag, and image.health still compares the stamp on the running machine with the manifest's. Proven: the tree manifest is byte-identical across a bump from 0.0.1 to 0.0.2 (identity a64e51b8e5ecca0af683d4f0 either way, the meta-forgefirm layer hash unchanged), where before the two differed. bitbake resolves FORGEFIRM_RELEASE=0.0.1 and FORGEFIRM_VERSION_STRING=v0.0.1 for the release image through the new require, and the dev image still overrides the string with its build timestamp.
161 lines
8.8 KiB
BlitzBasic
161 lines
8.8 KiB
BlitzBasic
require recipes-glowforge/images/glowforge-image.bb
|
|
|
|
# /etc/forgefirm-manifest.json: the build-input identity the acceptance tool
|
|
# and the release gate compare (forgefirm-image-manifest.bbclass).
|
|
inherit forgefirm-image-manifest
|
|
|
|
DESCRIPTION = "OpenGlow/ForgeFIRM image for Glowforge"
|
|
|
|
# ForgeFIRM cuts the cloud dependency: drop the Glowforge cloud client
|
|
# (gfui-client connects to Glowforge's servers). Removing it here (override
|
|
# only; the shared glowforge-image base is untouched) also sidesteps its
|
|
# do_package failure. Its role is filled locally by forgectrl and the
|
|
# controllers below. nano is a bench convenience: on the release rootfs it
|
|
# costs 8.7 MB, most of it libmagic and its database, which nothing else
|
|
# here uses. The dev image blanks FORGEFIRM_RELEASE_TRIM to keep it (a
|
|
# removal spec is expanded when applied, so the variable, not the removal,
|
|
# is what a requiring recipe can override). python3 is the meta-package:
|
|
# it installs every standard-library module (tkinter, idle, 2to3, pydoc,
|
|
# ensurepip, venv, the debugger, asyncio, multiprocessing, xmlrpc), which
|
|
# nothing here imports. Each Python recipe declares the module packages it
|
|
# imports (python3-core plus the few it uses), so the meta-package goes.
|
|
FORGEFIRM_RELEASE_TRIM ?= "nano"
|
|
IMAGE_INSTALL:remove = "python3 ${FORGEFIRM_RELEASE_TRIM}"
|
|
|
|
# grblhal-glowforge: the grblHAL motion controller (Grbl over TCP:23).
|
|
# forgectrl: the ForgeFIRM machine-services daemon (HTTP :80, HTTPS :443
|
|
# with a self-signed certificate): controller supervisor, pulse-device
|
|
# broker, cooling engine, cameras, telemetry, settings, diagnostics, web
|
|
# control panel, and A/B updates.
|
|
# gfhome: one-shot Glowforge web-service homing, invoked by the controller
|
|
# for $H when homing_mode = gfcloud (/data/forgefirm/forgefirm.conf).
|
|
# gfcloud: full Glowforge web-service controller daemon (the factory cloud
|
|
# experience), started when controller_mode = cloud - mutually exclusive with
|
|
# grblHAL. Pulls python3-ffmachine (shared web-service machine glue).
|
|
# v4l-utils provides media-ctl / v4l2-ctl for the imx-media pipeline (also a
|
|
# forgectrl runtime dependency, kept explicit here for bring-up use).
|
|
# fwup: applies signed .fw archives (ForgeFIRM upgrades + factory restore)
|
|
# to the inactive rootfs slot.
|
|
# ffboot: boot-slot inventory and switching (also ships fw_env.config).
|
|
# slotmigrate: boot-time reclaim of the legacy p4 layout (grows /data).
|
|
# forgefirm-logging: the ForgeFIRM logging tree - renders the per-logger
|
|
# rsyslog rules from the settings before rsyslog starts, and drives
|
|
# size-capped rotation (boot + hourly; a full /data breaks settings,
|
|
# updates, and controller writes). rsyslog itself comes in through
|
|
# VIRTUAL-RUNTIME_base-utils-syslog (conf/distro/forgefirm.conf).
|
|
IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome gfcloud v4l-utils fwup ffboot slotmigrate forgefirm-logging"
|
|
|
|
# forgefirm-users: renders the operator account record
|
|
# (/data/forgefirm/users, written by forgectrl) into the system account
|
|
# files at boot, before sshd, and on reload; also installs the warning an
|
|
# interactive root shell prints. forgefirm-banner: keeps the control
|
|
# panel addresses in the serial-console banner (/etc/issue).
|
|
# forgefirm-persist: the boot timestamp and the random seed on /data.
|
|
# avahi-daemon: mDNS, so the panel answers at https://forgefirm.local/
|
|
# and shows up in service browsers. The daemon is installed by name (the
|
|
# zeroconf distro feature stays off: it would bring libnss-mdns); the
|
|
# build options and the configuration are in conf/distro/forgefirm.conf
|
|
# and recipes-connectivity/avahi.
|
|
IMAGE_INSTALL:append = " forgefirm-users forgefirm-banner forgefirm-persist avahi-daemon"
|
|
|
|
# The rootfs mounts read-only on both images; /data (p3) is the writable
|
|
# partition. read-only-rootfs is poky's feature for it: the root line of
|
|
# /etc/fstab (the BSP's, already ro) and ROOTFS_READ_ONLY in
|
|
# /etc/default/rcS, the volatile links made at rootfs time
|
|
# (populate-volatile.sh: /etc/resolv.conf, /tmp), a writable copy of
|
|
# /var/lib at boot (read-only-rootfs-hook.sh), a build failure for a
|
|
# package whose post-install must run on the machine, and the removal of
|
|
# the packages a read-only rootfs cannot use (shadow, base-passwd,
|
|
# update-rc.d, update-alternatives; the account files stay). What must
|
|
# last or change at run time is handled file by file: the account files
|
|
# and /etc/issue (forgefirm-users, forgefirm-banner), the sshd host keys
|
|
# (recipes-connectivity/openssh), the timestamp and the random seed
|
|
# (forgefirm-persist). The facts are on the docs site,
|
|
# technical/forgefirm/image-and-bsp; scripts/release.sh checks the built
|
|
# rootfs for this state.
|
|
IMAGE_FEATURES += "read-only-rootfs"
|
|
|
|
# Root policy. root has no password and logs in at the serial console
|
|
# only: that is the recovery path when the network, the panel or an
|
|
# account is broken, and the console is behind the case. Over the
|
|
# network, sshd refuses root (PermitRootLogin no) and any account without
|
|
# a password (PermitEmptyPasswords no), both set by
|
|
# recipes-connectivity/openssh, and sshd runs only when the panel turns
|
|
# it on. Operator logins are the accounts in the record (forgefirm-users).
|
|
# empty-root-password keeps the rootfs postprocess from locking root
|
|
# (zap_empty_root_password in rootfs-postcommands.bbclass); it is not
|
|
# debug-tweaks, which belongs to the dev image alone and would open SSH.
|
|
# scripts/release.sh checks the built rootfs for exactly this state.
|
|
IMAGE_FEATURES += "empty-root-password"
|
|
|
|
# Mesa GLES2/EGL on etnaviv for forgectrl's GPU demosaic (loaded with
|
|
# dlopen at runtime; forgectrl itself has no build-time GL dependency,
|
|
# and without these packages it falls back to the NEON path).
|
|
IMAGE_INSTALL:append = " libegl-mesa libgles2-mesa libgbm mesa-megadriver"
|
|
|
|
# NXP's firmware EULA covers the i.MX VPU/EPDC blobs the BSP installs, so the
|
|
# image ships the license text with them (/usr/share/licenses/firmware-imx).
|
|
# The SDMA firmware brings its own -license package through linux-firmware.
|
|
IMAGE_INSTALL:append = " firmware-imx-lic"
|
|
|
|
# The release rootfs must fit a 200 MiB factory eMMC slot (409600 blocks).
|
|
# Sizing: content + 40 MiB working space, hard-capped at the slot size:
|
|
# the build fails rather than emit an unflashable image. The raw ext4 is
|
|
# deployed alongside the wic; scripts/mkfw.sh packs it into the signed
|
|
# .fw release artifact.
|
|
IMAGE_FSTYPES:append = " ext4"
|
|
IMAGE_OVERHEAD_FACTOR = "1.0"
|
|
IMAGE_ROOTFS_EXTRA_SPACE = "40960"
|
|
IMAGE_ROOTFS_MAXSIZE = "204800"
|
|
|
|
# Version stamp: /etc/forgefirm-version (machine-readable), echoed on the
|
|
# serial-console login prompt (/etc/issue) and at SSH login (motd).
|
|
# Release images carry the release version; the dev image overrides the
|
|
# string with the build timestamp (the same DATETIME as the artifact
|
|
# name) plus a dev tag.
|
|
#
|
|
# FORGEFIRM_RELEASE lives in its own file, which the manifest leaves out
|
|
# of the layer content hash: the version is metadata, and a bump must not
|
|
# read as a platform change and invalidate a campaign
|
|
# (forgefirm-release.inc).
|
|
require forgefirm-release.inc
|
|
FORGEFIRM_VERSION_STRING ?= "v${FORGEFIRM_RELEASE}"
|
|
|
|
write_forgefirm_version() {
|
|
echo "${FORGEFIRM_VERSION_STRING}" > ${IMAGE_ROOTFS}${sysconfdir}/forgefirm-version
|
|
echo "ForgeFIRM ${FORGEFIRM_VERSION_STRING}" >> ${IMAGE_ROOTFS}${sysconfdir}/issue
|
|
echo "" >> ${IMAGE_ROOTFS}${sysconfdir}/issue
|
|
echo "ForgeFIRM ${FORGEFIRM_VERSION_STRING}" > ${IMAGE_ROOTFS}${sysconfdir}/motd
|
|
}
|
|
write_forgefirm_version[vardepsexclude] += "DATETIME"
|
|
# No semicolon after a function name here or below (the vardeps rule in
|
|
# classes/forgefirm-image-manifest.bbclass).
|
|
ROOTFS_POSTPROCESS_COMMAND += "write_forgefirm_version "
|
|
|
|
# The license texts ride with the software. The license class writes
|
|
# the image's license manifest (every installed package with its
|
|
# license) and copies each package's license texts into
|
|
# /usr/share/common-licenses, one copy of each generic text and
|
|
# symlinks to it per package. That tree costs several megabytes of
|
|
# small files on a raw ext4 rootfs, so it is packed into one
|
|
# reproducible tar.gz (sorted names, no timestamps, no owners) at
|
|
# /usr/share/forgefirm/licenses.tar.gz and the tree is removed. The
|
|
# control panel serves the bundle and its manifest (GET /system/licenses,
|
|
# GET /system/licenses/manifest). The license class runs first
|
|
# (license_create_manifest is prepended to this list); this step is
|
|
# appended, so it runs after.
|
|
COPY_LIC_MANIFEST = "1"
|
|
COPY_LIC_DIRS = "1"
|
|
|
|
pack_licenses() {
|
|
d="${IMAGE_ROOTFS}${datadir}/common-licenses"
|
|
[ -d "$d" ] || bbfatal "pack_licenses: $d is missing (COPY_LIC_DIRS off?)"
|
|
[ -f "$d/license.manifest" ] || bbfatal "pack_licenses: no license.manifest in $d"
|
|
install -d "${IMAGE_ROOTFS}${datadir}/forgefirm"
|
|
tar -C "${IMAGE_ROOTFS}${datadir}" --sort=name --mtime=@0 --owner=0 --group=0 \
|
|
--numeric-owner -cf - common-licenses | gzip -9 -n \
|
|
> "${IMAGE_ROOTFS}${datadir}/forgefirm/licenses.tar.gz"
|
|
rm -rf "$d"
|
|
}
|
|
ROOTFS_POSTPROCESS_COMMAND += "pack_licenses "
|