Files
pxe-server/scripts/stage-usb-share.sh
cproudlock 04db6d6605 Generic build for other GE sites: no WJ data, one shopfloor type, Windows USB builder
Everything West Jefferson specific moved out to a separate repo
(pxe-site-wj): NTLARS/UDC/heat-treat backups keyed by WJ machine numbers,
CMM and Wax/Trace bay lists, WJ OpenText profiles, site-config.json, the
WJ preinstall list, and the WJ shopfloor PC types (Collections,
NoCollections, Common, Keyence, CMM, Genspect, HeatTreat, Wax/Trace,
Display, PartMarker) with GE-Enforce, machine numbers and the desktop
layout. The part-marker bench tools moved to their own repo. Dead
scripts removed: build-proxmox-iso, deploy-bios, sync_hardware_models,
mirror-from-gold, pull-bios, startnet-template.cmd.

Shopfloor is one generic type, gea-shopfloor-standard, with today's
timing:
- PPKG at first logon (unattend Order 10, unchanged).
- Install-AppList.ps1 runs C:\PreInstall\preinstall.json during setup.
- A new SYSTEM task, GE Shopfloor Post-PPKG, runs postppkg.json once
  after the post-PPKG reboot. It also clears AutoLogonCount after the
  ShopFloor handover (the Users-level monitor can be denied that HKLM
  write) and removes the Intune Sync task when done.
- Both lists ship empty. A site adds entries and installers.
- WinPE choice 3 goes straight to the PPKG menu. Staging, fetch and heal
  carry only the generic payload. The webapp's shopfloor sub-menu editor
  is gone, and the imaging tiles name the new stages.
- The playbook deploys the empty lists only if missing. It now deploys
  the PowerShell 7 MSI the shopfloor unattend installs, which it never
  did before.

Blancco site settings move into the webapp (Updates > Blancco releases):
business location, licensing Wi-Fi SSID and password. They are rendered
into the served preferences.xml and applied to every staged release, and
are seeded from site-vars (site_name, blancco_wifi_ssid,
blancco_wifi_password) only while none exist. The repo XML carries no
site values; the plaintext Wi-Fi password is gone from it.

New site_name and site_timezone settings: report and evidence-pack site
name, server clock and webapp time zone. No more West Jefferson defaults
in webapp/config.py or the dashboard title. Sync-BPRT.ps1 gets the
server address filled in at download instead of WJ's GE address.

USB on Windows:
- scripts/Build-PxeUsb.ps1 runs the same checks as build-usb.sh. It
  writes one FAT32 CIDATA partition (ISO files plus patched grub.cfg plus
  payload) with diskpart and reads every file back by SHA-256.
- Tested with Windows PowerShell 5.1 in the win11 VM, and the layout
  installs and first-boots in the pxetest VM.
- scripts/stage-usb-share.sh stages the build folder on a share.
- site-content/ is optional for both builders.

KB docs/kb/How_To_Deploy_PXE_Server_New_Site.html: new-site deployment,
Windows only, with a site-vars.yml guide.

Checks: webapp tests pass (5 suites); the unattend lint and playbook
syntax checks pass; every .ps1 parses; there are no WJ hosts, IPs, names
or PC types in playbook/, webapp/, scripts/ or autoinstall/.
2026-10-16 20:37:22 -04:00

109 lines
4.3 KiB
Bash
Executable File

#!/bin/bash
#
# stage-usb-share.sh - Put everything Build-PxeUsb.ps1 needs into a folder
# on a share, so a Windows PC can build the installer USB from it.
#
# Usage:
# scripts/stage-usb-share.sh [--iso FILE] [--payload-root DIR] [DEST]
#
# DEST defaults to ~/pxe-images/pxe-usb (the [pxe-images] Samba share; on
# the work PC: \\<this PC>\pxe-images\pxe-usb, usually Z:\pxe-usb). It gets:
# - the repo as committed (git archive HEAD: uncommitted edits are NOT
# staged, so the stick matches a commit);
# - the payload folders (offline-packages, pip-wheels, boot-files,
# boot-tools, enrollment, bios-staging, drivers-staging, site-content)
# as hard links, so they cost no extra disk space;
# - the Ubuntu ISO offline-packages/ was built for (hard link);
# - STAGED.txt: commit, date, sizes.
# Then on the work PC: copy the folder to a local disk (C:\pxe-usb) and
# double-click Build-PxeUsb.cmd (KB: docs/kb/How_To_Deploy_PXE_Server_New_Site.html).
#
# The new copy is built beside the old one and swapped in when complete.
# Run capture-site-content.sh --apply first so site-content/ is current.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")"/.. && pwd)"
PAYLOAD_ROOT="$REPO_ROOT"
ISO=""
DEST=""
while [ $# -gt 0 ]; do
case "$1" in
--iso) ISO="${2:?}"; shift 2 ;;
--payload-root) PAYLOAD_ROOT="$(cd "${2:?}" && pwd)"; shift 2 ;;
-h|--help) sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
-*) echo "unknown option $1" >&2; exit 2 ;;
*) DEST="$1"; shift ;;
esac
done
DEST="${DEST:-$HOME/pxe-images/pxe-usb}"
mkdir -p "$(dirname "$DEST")"
DEST="$(cd "$(dirname "$DEST")" && pwd)/$(basename "$DEST")"
# Only ever replace a folder this script made (it is swapped and deleted).
if [ -e "$DEST" ] && [ ! -f "$DEST/STAGED.txt" ]; then
echo "ERROR: $DEST exists and was not made by this script (no STAGED.txt); refusing to replace it" >&2
exit 1
fi
# The ISO the offline set was resolved for (BUILD-INFO names it).
if [ -z "$ISO" ]; then
name=$(sed -n 's/.*--iso \([^ ]*\.iso\).*/\1/p' "$PAYLOAD_ROOT/offline-packages/BUILD-INFO.txt" 2>/dev/null | head -1)
name=$(basename "${name:-}")
for d in "$PAYLOAD_ROOT" "$PAYLOAD_ROOT/iso" "${PXETEST_CACHE:-$HOME/.cache/pxetest}"; do
[ -n "$name" ] && [ -f "$d/$name" ] && { ISO="$d/$name"; break; }
done
[ -n "$ISO" ] || { echo "ERROR: give --iso FILE (the offline set was built for ${name:-an unknown ISO})" >&2; exit 1; }
fi
[ -f "$ISO" ] || { echo "ERROR: ISO not found: $ISO" >&2; exit 1; }
same_fs() { [ "$(stat -c %d "$1")" = "$(stat -c %d "$2")" ]; }
NEW="$DEST.new.$$"
OLD="$DEST.old.$$"
cleanup() { [ -d "$NEW" ] && rm -rf -- "$NEW"; return 0; }
trap cleanup EXIT
mkdir -p "$NEW"
cd "$REPO_ROOT"
COMMIT=$(git rev-parse --short HEAD)
if ! git diff --quiet HEAD -- autoinstall playbook scripts webapp; then
echo "NOTE: uncommitted changes in autoinstall/playbook/scripts/webapp are NOT staged (HEAD $COMMIT is)."
fi
echo "Repo $COMMIT -> $NEW"
git archive HEAD | tar -x -C "$NEW"
link_or_copy() { # SRC DST
if same_fs "$1" "$(dirname "$2")"; then cp -al "$1" "$2"; else cp -a "$1" "$2"; fi
}
for d in offline-packages pip-wheels boot-files boot-tools enrollment bios-staging drivers-staging site-content; do
src="$PAYLOAD_ROOT/$d"
[ -d "$src" ] || { echo " $d: not in $PAYLOAD_ROOT (skipped)"; continue; }
# git archive brings the tracked bits of boot-files/ and boot-tools/; the
# payload folder replaces them whole.
rm -rf -- "${NEW:?}/$d"
link_or_copy "$src" "$NEW/$d"
printf ' %-17s %s\n' "$d" "$(du -sh "$NEW/$d" | cut -f1)"
done
link_or_copy "$ISO" "$NEW/$(basename "$ISO")"
echo " ISO $(basename "$ISO")"
{
echo "Staged by scripts/stage-usb-share.sh $(date -Is)"
echo "Commit: $(git log -1 --format='%h %s')"
echo "ISO: $(basename "$ISO")"
echo "Payload root: $PAYLOAD_ROOT"
echo
echo "On the Windows PC:"
echo " 1. Copy this whole folder to a local disk, e.g. C:\\pxe-usb"
echo " 2. Double-click C:\\pxe-usb\\Build-PxeUsb.cmd (asks for admin, lists USB disks)"
echo " Instructions: docs\\kb\\How_To_Deploy_PXE_Server_New_Site.html"
} >"$NEW/STAGED.txt"
if [ -e "$DEST" ]; then mv -- "$DEST" "$OLD"; fi
mv -- "$NEW" "$DEST"
[ -e "$OLD" ] && rm -rf -- "$OLD"
trap - EXIT
echo "Staged: $DEST ($(du -sh "$DEST" | cut -f1) apparent, hard-linked payload)"