Compare commits

..

2 Commits

Author SHA1 Message Date
nearxos
55b8661a2e Update documentation and scripts for revision tracking and cloud-init enhancements</message>
<message>Introduce a revision tracking system across project files, allowing for easier identification of changes. Update the README files to include instructions for bumping revisions and auto-bumping on commits. Additionally, enhance cloud-init scripts with revision comments for better version control. Modify the dashboard API to improve build status management, including a forced clear option for stuck statuses, enhancing user experience and operational reliability.
2026-02-23 10:38:24 +02:00
nearxos
5f05663706 Implement graceful cancellation for cloud-init image compression</message>
<message>Add a cleanup function to handle cancellation of the xz compression process in the build-cloudinit-image.sh script. This enhancement allows for a more robust response to cancellation requests, ensuring that resources are properly released and status messages are updated accordingly. The script now traps termination signals and cleans up temporary files, improving the overall reliability of the cloud-init image building workflow.
2026-02-23 10:32:07 +02:00
27 changed files with 177 additions and 12 deletions

View File

@@ -1,7 +1,18 @@
<!-- Revision: 2 -->
# reTerminal DM4 # reTerminal DM4
Project for **reTerminal DM4** (Seeed) with CM4: Chromium kiosk, eMMC provisioning (USB + network boot), and first-boot configuration via cloud-init. Project for **reTerminal DM4** (Seeed) with CM4: Chromium kiosk, eMMC provisioning (USB + network boot), and first-boot configuration via cloud-init.
## Revisions
A single **revision number** is kept in `REVISION` and in a comment line in tracked files (`# Revision: N` or `<!-- Revision: N -->`) so you can see what changed across hosts and deploys.
- **Bump revision (update all files):** from repo root run
`./emmc-provisioning/scripts/bump-revision.sh`
- **Auto-bump on every commit:** install the pre-commit hook
`cp emmc-provisioning/scripts/pre-commit-revision.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit`
Then every commit will bump the revision and update the revision line in all tracked files.
## Repository structure ## Repository structure
| Path | Purpose | | Path | Purpose |

1
REVISION Normal file
View File

@@ -0,0 +1 @@
2

View File

@@ -1,7 +1,10 @@
<!-- Revision: 2 -->
# reTerminal DM4 eMMC provisioning # reTerminal DM4 eMMC provisioning
Automatically **deploy** or **backup** the CM4 eMMC when the reTerminal is connected in **USB boot mode** or when it **boots over the network**. Uses **cloud-init** for first-boot configuration. Automatically **deploy** or **backup** the CM4 eMMC when the reTerminal is connected in **USB boot mode** or when it **boots over the network**. Uses **cloud-init** for first-boot configuration.
Revisions are tracked project-wide; see repo root **README.md** and `scripts/bump-revision.sh`.
--- ---
## Project layout ## Project layout

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Revision: 2
# One-shot: set screen rotation via kanshi (same as Control Center), then remove self. # One-shot: set screen rotation via kanshi (same as Control Center), then remove self.
# Reads video=DSI-1:rotate=N from kernel cmdline and writes ~/.config/kanshi/config. # Reads video=DSI-1:rotate=N from kernel cmdline and writes ~/.config/kanshi/config.
# Runs once as user pi at first login; deletes its autostart and this script so it never runs again. # Runs once as user pi at first login; deletes its autostart and this script so it never runs again.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Revision: 2
# One-shot: set desktop wallpaper for labwc (swaybg) and persist via labwc autostart, then remove self. # One-shot: set desktop wallpaper for labwc (swaybg) and persist via labwc autostart, then remove self.
# Runs as user pi at first login. Logs to /var/log/first-boot.log. # Runs as user pi at first login. Logs to /var/log/first-boot.log.
FIRST_BOOT_LOG="/var/log/first-boot.log" FIRST_BOOT_LOG="/var/log/first-boot.log"

View File

@@ -1,4 +1,5 @@
# first-boot.conf.example # Revision: 2
# first-boot.conf (or .example)
# Copy to first-boot.conf and edit. Loaded by first-boot.sh from: # Copy to first-boot.conf and edit. Loaded by first-boot.sh from:
# - same directory as first-boot.sh, or # - same directory as first-boot.sh, or
# - /tmp/first-boot.conf (when run via cloud-init), or # - /tmp/first-boot.conf (when run via cloud-init), or

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Revision: 2
# First-boot: packages, Chromium kiosk, rpd-labwc + touch, reTerminal DM drivers. # First-boot: packages, Chromium kiosk, rpd-labwc + touch, reTerminal DM drivers.
# Run by cloud-init (user-data-remote-gnss.example). Run as root. # Run by cloud-init (user-data-remote-gnss.example). Run as root.
# Optional: copy first-boot.conf.example to first-boot.conf and edit; it is loaded # Optional: copy first-boot.conf.example to first-boot.conf and edit; it is loaded

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Revision: 2
# One-time fix for reTerminal DM after first-boot: splash, Plymouth theme, rotation, wallpaper. # One-time fix for reTerminal DM after first-boot: splash, Plymouth theme, rotation, wallpaper.
# Run on the device as root (e.g. sudo bash fix-reterminal-display.sh). # Run on the device as root (e.g. sudo bash fix-reterminal-display.sh).
# Or run over SSH: ssh pi@DEVICE_IP 'sudo bash -s' < fix-reterminal-display.sh # Or run over SSH: ssh pi@DEVICE_IP 'sudo bash -s' < fix-reterminal-display.sh

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Revision: 2
# Set screen rotation via kanshi (same as Control Center). Reads video=DSI-1:rotate=N # Set screen rotation via kanshi (same as Control Center). Reads video=DSI-1:rotate=N
# from kernel cmdline and writes ~/.config/kanshi/config. Kanshi auto-reloads when the file changes. # from kernel cmdline and writes ~/.config/kanshi/config. Kanshi auto-reloads when the file changes.
# Also sets GTK dark theme (PiXnoir / Adwaita-dark). Runs from autostart when user pi logs in; does not remove itself. # Also sets GTK dark theme (PiXnoir / Adwaita-dark). Runs from autostart when user pi logs in; does not remove itself.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Revision: 2
# Start Chromium in app mode. Optional env vars: # Start Chromium in app mode. Optional env vars:
# CHROMIUM_APP_URL - URL to open (default: http://127.0.0.1:8080) # CHROMIUM_APP_URL - URL to open (default: http://127.0.0.1:8080)
# CHROMIUM_MODE - "fullscreen" or "kiosk" (default: fullscreen) # CHROMIUM_MODE - "fullscreen" or "kiosk" (default: fullscreen)

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Revision: 2
""" """
Flask dashboard for CM4 eMMC provisioning. Flask dashboard for CM4 eMMC provisioning.
Public home: deploy only (status, logs, how to connect). No login. Public home: deploy only (status, logs, how to connect). No login.
@@ -1632,10 +1633,12 @@ def api_build_cloudinit_cancel():
@app.route("/api/build-cloudinit-status-clear", methods=["POST"]) @app.route("/api/build-cloudinit-status-clear", methods=["POST"])
@require_admin @require_admin
def api_build_cloudinit_status_clear(): def api_build_cloudinit_status_clear():
"""Clear build status to idle (so message is cleared after cancel/done/error).""" """Clear build status to idle (so message is cleared after cancel/done/error).
Use ?force=1 to clear even when status is stuck (e.g. build died during finalizing)."""
st = _build_status_read() st = _build_status_read()
force = request.args.get("force") in ("1", "true", "yes")
busy = st.get("phase") in ("downloading", "decompressing", "injecting", "finalizing", "resolving") busy = st.get("phase") in ("downloading", "decompressing", "injecting", "finalizing", "resolving")
if busy: if busy and not force:
return jsonify({"ok": False, "error": "Cannot clear while build is in progress"}), 409 return jsonify({"ok": False, "error": "Cannot clear while build is in progress"}), 409
_build_status_write("idle", "", None, None) _build_status_write("idle", "", None, None)
return jsonify({"ok": True}) return jsonify({"ok": True})

View File

@@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- Revision: 2 -->
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">

View File

@@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- Revision: 2 -->
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -155,7 +156,7 @@
if (!window._buildClearScheduled) { if (!window._buildClearScheduled) {
window._buildClearScheduled = true; window._buildClearScheduled = true;
setTimeout(function() { setTimeout(function() {
authFetch('/api/build-cloudinit-status-clear', { method: 'POST', headers: {'Content-Type':'application/json'} }).then(function() { fetchBuildStatus(); }).finally(function() { window._buildClearScheduled = false; }); authFetch('/api/build-cloudinit-status-clear?force=1', { method: 'POST', headers: {'Content-Type':'application/json'} }).then(function() { fetchBuildStatus(); }).finally(function() { window._buildClearScheduled = false; });
}, 3000); }, 3000);
} }
} else { } else {
@@ -243,7 +244,7 @@
var cancelBuildBtn = document.getElementById('buildCloudInitCancelBtn'); var cancelBuildBtn = document.getElementById('buildCloudInitCancelBtn');
if (cancelBuildBtn) cancelBuildBtn.onclick = cancelBuild; if (cancelBuildBtn) cancelBuildBtn.onclick = cancelBuild;
var dismissBuildBtn = document.getElementById('buildCloudInitDismiss'); var dismissBuildBtn = document.getElementById('buildCloudInitDismiss');
if (dismissBuildBtn) dismissBuildBtn.onclick = function(e) { e.preventDefault(); authFetch('/api/build-cloudinit-status-clear', { method: 'POST', headers: {'Content-Type':'application/json'} }).then(function() { fetchBuildStatus(); }); }; if (dismissBuildBtn) dismissBuildBtn.onclick = function(e) { e.preventDefault(); authFetch('/api/build-cloudinit-status-clear?force=1', { method: 'POST', headers: {'Content-Type':'application/json'} }).then(function() { fetchBuildStatus(); }); };
document.getElementById('buildVariant').onchange = function() { document.getElementById('buildVariant').onchange = function() {
authFetch('/api/raspios-latest-url?variant=' + encodeURIComponent(document.getElementById('buildVariant').value)).then(function(r) { return r.json(); }).then(function(d) { authFetch('/api/raspios-latest-url?variant=' + encodeURIComponent(document.getElementById('buildVariant').value)).then(function(r) { return r.json(); }).then(function(d) {
document.getElementById('buildRaspiosUrl').textContent = (d.ok && d.filename) ? d.filename : (d.error || ''); document.getElementById('buildRaspiosUrl').textContent = (d.ok && d.filename) ? d.filename : (d.error || '');

View File

@@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- Revision: 2 -->
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">

View File

@@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- Revision: 2 -->
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -916,7 +917,7 @@
if (!window._buildClearScheduled) { if (!window._buildClearScheduled) {
window._buildClearScheduled = true; window._buildClearScheduled = true;
setTimeout(function() { setTimeout(function() {
fetch('/api/build-cloudinit-status-clear', { method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(function() { fetchBuildStatus(); }).finally(function() { window._buildClearScheduled = false; }); fetch('/api/build-cloudinit-status-clear?force=1', { method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(function() { fetchBuildStatus(); }).finally(function() { window._buildClearScheduled = false; });
}, 3000); }, 3000);
} }
} else { } else {
@@ -1024,7 +1025,7 @@
var cancelBuildBtn = document.getElementById('buildCloudInitCancelBtn'); var cancelBuildBtn = document.getElementById('buildCloudInitCancelBtn');
if (cancelBuildBtn) cancelBuildBtn.onclick = cancelBuildCloudInit; if (cancelBuildBtn) cancelBuildBtn.onclick = cancelBuildCloudInit;
var dismissBuildBtn = document.getElementById('buildCloudInitDismiss'); var dismissBuildBtn = document.getElementById('buildCloudInitDismiss');
if (dismissBuildBtn) dismissBuildBtn.onclick = function(e) { e.preventDefault(); fetch('/api/build-cloudinit-status-clear', { method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(function() { fetchBuildStatus(); }); }; if (dismissBuildBtn) dismissBuildBtn.onclick = function(e) { e.preventDefault(); fetch('/api/build-cloudinit-status-clear?force=1', { method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(function() { fetchBuildStatus(); }); };
var variantSel = document.getElementById('buildVariant'); var variantSel = document.getElementById('buildVariant');
if (variantSel) variantSel.onchange = fetchRaspiosUrl; if (variantSel) variantSel.onchange = fetchRaspiosUrl;
var templateLoadBtn = document.getElementById('buildTemplateLoad'); var templateLoadBtn = document.getElementById('buildTemplateLoad');

View File

@@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- Revision: 2 -->
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">

View File

@@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- Revision: 2 -->
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">

View File

@@ -1,3 +1,4 @@
<!-- Revision: 2 -->
# Host-side provisioning scripts # Host-side provisioning scripts
These files run on the **provisioning host** (e.g. the Proxmox host where the reTerminal is connected via USB). These files run on the **provisioning host** (e.g. the Proxmox host where the reTerminal is connected via USB).
@@ -9,3 +10,23 @@ These files run on the **provisioning host** (e.g. the Proxmox host where the re
| **90-cm4-boot-mode.rules** | udev rule: on USB add (vendor 2b8e), run the trigger. Install to `/etc/udev/rules.d/`. | | **90-cm4-boot-mode.rules** | udev rule: on USB add (vendor 2b8e), run the trigger. Install to `/etc/udev/rules.d/`. |
See [../docs/EMMC-PROVISIONING-GUIDE.md](../docs/EMMC-PROVISIONING-GUIDE.md) for full setup. The [deploy script](../scripts/deploy-to-proxmox.sh) copies these into place on the Proxmox host and LXC. See [../docs/EMMC-PROVISIONING-GUIDE.md](../docs/EMMC-PROVISIONING-GUIDE.md) for full setup. The [deploy script](../scripts/deploy-to-proxmox.sh) copies these into place on the Proxmox host and LXC.
### Stuck “finalizing: Compressing image (xz)…” on the host
If the dashboard still shows that message, run on the **host** (e.g. `ssh root@10.20.30.152`):
```bash
# 1) See current status and whether build/xz is still running
PROV="${CM4_PROVISIONING_DIR:-/var/lib/cm4-provisioning}"
cat "$PROV/build_cloudinit_status.json"
ps aux | grep -E 'build-cloudinit|xz.*\.img' | grep -v grep
# 2a) If a build is still running and you want to cancel it
touch "$PROV/build_cloudinit_cancel"
# Script checks every 2s; after it exits, status will show "cancelled"
# 2b) If no build process is running but status is stuck, reset to idle
echo '{"phase":"idle","message":"","output_name":null,"error":null,"updated":'$(date +%s)'}' > "$PROV/build_cloudinit_status.json"
```
Then refresh the dashboard or click **Dismiss** to clear the message.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Revision: 2
# Run on the Proxmox host when build_cloudinit_request.json appears in the provisioning dir. # Run on the Proxmox host when build_cloudinit_request.json appears in the provisioning dir.
# Downloads Raspberry Pi OS image from URL, injects cloud-init NoCloud files, saves to BACKUPS_DIR. # Downloads Raspberry Pi OS image from URL, injects cloud-init NoCloud files, saves to BACKUPS_DIR.
# Uses loop devices and mount (available on host, not in unprivileged LXC). # Uses loop devices and mount (available on host, not in unprivileged LXC).
@@ -193,21 +194,31 @@ cp "$IMG_FILE" "$OUT_PATH"
check_cancel check_cancel
write_status "finalizing" "Compressing image (xz)…" "" "" write_status "finalizing" "Compressing image (xz)…" "" ""
OUT_XZ="${OUT_PATH}.xz" OUT_XZ="${OUT_PATH}.xz"
XZ_PID=""
cleanup_xz_and_exit() {
local phase="${1:-cancelled}"
if [[ -n "$XZ_PID" ]] && kill -0 "$XZ_PID" 2>/dev/null; then
kill "$XZ_PID" 2>/dev/null
wait "$XZ_PID" 2>/dev/null || true
fi
write_status "$phase" "Build cancelled." "" ""
rm -f "$REQUEST_FILE" "$CANCEL_FILE"
exit 0
}
trap 'cleanup_xz_and_exit "cancelled"' TERM INT
xz -T 0 -z -k -f "$OUT_PATH" 2>/dev/null & xz -T 0 -z -k -f "$OUT_PATH" 2>/dev/null &
XZ_PID=$! XZ_PID=$!
while kill -0 "$XZ_PID" 2>/dev/null; do while kill -0 "$XZ_PID" 2>/dev/null; do
if [[ -f "$CANCEL_FILE" ]]; then if [[ -f "$CANCEL_FILE" ]]; then
kill "$XZ_PID" 2>/dev/null cleanup_xz_and_exit "cancelled"
wait "$XZ_PID" 2>/dev/null
write_status "cancelled" "Build cancelled." "" ""
rm -f "$REQUEST_FILE" "$CANCEL_FILE"
exit 0
fi fi
sleep 2 sleep 2
done done
wait "$XZ_PID" 2>/dev/null || true wait "$XZ_PID" 2>/dev/null || true
trap - TERM INT
if [[ ! -f "$OUT_XZ" ]]; then if [[ ! -f "$OUT_XZ" ]]; then
xz -T 1 -z -k -f "$OUT_PATH" 2>/dev/null || true xz -T 1 -z -k -f "$OUT_PATH" 2>/dev/null || true
fi fi
if [[ -f "$OUT_XZ" ]]; then if [[ -f "$OUT_XZ" ]]; then
rm -f "$OUT_PATH" rm -f "$OUT_PATH"

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Revision: 2
# Called by udev when CM4 in boot mode (0a5c:2711 or 2b8e) is connected. # Called by udev when CM4 in boot mode (0a5c:2711 or 2b8e) is connected.
# Start the flash service via systemd so it runs under systemd, not udev — # Start the flash service via systemd so it runs under systemd, not udev —
# otherwise udev kills the process when the device re-enumerates (first→second stage). # otherwise udev kills the process when the device re-enumerates (first→second stage).

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Revision: 2
# Provision CM4 eMMC when reTerminal is connected in boot mode (eMMC disable jumper). # Provision CM4 eMMC when reTerminal is connected in boot mode (eMMC disable jumper).
# On USB boot device (2b8e) detection: run rpiboot to expose eMMC, then wait for the user # On USB boot device (2b8e) detection: run rpiboot to expose eMMC, then wait for the user
# to choose Backup or Deploy in the portal — no auto-flash; action runs only after portal choice. # to choose Backup or Deploy in the portal — no auto-flash; action runs only after portal choice.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Revision: 2
# Run on the Proxmox host when shrink_request.json appears in the provisioning dir. # Run on the Proxmox host when shrink_request.json appears in the provisioning dir.
# Runs PiShrink on the requested backup image (shrink or shrink+compress). # Runs PiShrink on the requested backup image (shrink or shrink+compress).
# Requires PiShrink on host (scripts/install-pishrink-on-host.sh). Triggered by cm4-shrink.path. # Requires PiShrink on host (scripts/install-pishrink-on-host.sh). Triggered by cm4-shrink.path.

View File

@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# Revision: 2
# Bumps REVISION at repo root and updates the "Revision: N" line in all tracked files.
# Run from repo root, or use with: ./emmc-provisioning/scripts/bump-revision.sh
# Optional: use in a pre-commit hook so every commit gets a new revision.
set -e
# Script lives in emmc-provisioning/scripts/; repo root is two levels up
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
REVISION_FILE="$ROOT/REVISION"
# Files to update (relative to ROOT). Each must contain a line matching:
# # Revision: N or <!-- Revision: N -->
TRACKED=(
emmc-provisioning/scripts/bump-revision.sh
emmc-provisioning/scripts/pre-commit-revision.sh
emmc-provisioning/host/build-cloudinit-image.sh
emmc-provisioning/host/flash-emmc-on-connect.sh
emmc-provisioning/host/run-shrink-on-host.sh
emmc-provisioning/host/README.md
emmc-provisioning/host/cm4-flash-trigger.sh
emmc-provisioning/dashboard/app.py
emmc-provisioning/dashboard/templates/index.html
emmc-provisioning/dashboard/templates/home.html
emmc-provisioning/dashboard/templates/cloudinit_build.html
emmc-provisioning/dashboard/templates/admin.html
emmc-provisioning/dashboard/templates/login.html
emmc-provisioning/dashboard/templates/portal_files.html
emmc-provisioning/cloud-init/first-boot.sh
emmc-provisioning/cloud-init/first-boot.conf
emmc-provisioning/cloud-init/start-chromium.sh
emmc-provisioning/cloud-init/01-set-rotation-once.sh
emmc-provisioning/cloud-init/02-set-wallpaper-once.sh
emmc-provisioning/cloud-init/set-rotation-at-login.sh
emmc-provisioning/cloud-init/fix-reterminal-display.sh
emmc-provisioning/scripts/deploy-to-proxmox.sh
emmc-provisioning/scripts/deploy-dashboard-to-lxc.sh
emmc-provisioning/scripts/sync-portal-files-to-lxc.sh
README.md
emmc-provisioning/README.md
)
current=1
[[ -f "$REVISION_FILE" ]] && current=$(awk '/^[0-9]+$/ { print $1; exit }' "$REVISION_FILE" || true)
[[ -z "$current" ]] && current=0
next=$((current + 1))
echo "$next" > "$REVISION_FILE"
for rel in "${TRACKED[@]}"; do
f="$ROOT/$rel"
[[ "$rel" == "REVISION" ]] && continue
[[ ! -f "$f" ]] && continue
if grep -qE 'Revision: [0-9]+|<!-- Revision: [0-9]+ -->' "$f" 2>/dev/null; then
sed -i -E 's/(Revision: )[0-9]+/\1'"$next"'/; s/(<!-- Revision: )[0-9]+( -->)/\1'"$next"'\2/' "$f"
fi
done
echo "Revision bumped to $next"

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Revision: 2
# Deploy only the dashboard to the LXC by IP (no Proxmox host needed). # Deploy only the dashboard to the LXC by IP (no Proxmox host needed).
# Uses rsync so all files (app, templates, service file, etc.) stay in sync. # Uses rsync so all files (app, templates, service file, etc.) stay in sync.
# Usage: ./deploy-dashboard-to-lxc.sh [user@lxc_ip] # Usage: ./deploy-dashboard-to-lxc.sh [user@lxc_ip]

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Revision: 2
# Deploy CM4 eMMC provisioning to a Proxmox host: host scripts, udev, systemd units, # Deploy CM4 eMMC provisioning to a Proxmox host: host scripts, udev, systemd units,
# LXC container (dashboard), usbboot (rpiboot), and PiShrink. Uses hostname "cm4-provisioning" # LXC container (dashboard), usbboot (rpiboot), and PiShrink. Uses hostname "cm4-provisioning"
# to find the container on redeploy; creates with next available ID if not found. # to find the container on redeploy; creates with next available ID if not found.

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Revision: 2
# Sample pre-commit hook: bump revision and update all tracked files, then stage them.
# Install: cp emmc-provisioning/scripts/pre-commit-revision.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
# Or: ln -sf ../../emmc-provisioning/scripts/pre-commit-revision.sh .git/hooks/pre-commit
set -e
ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"
"$ROOT/emmc-provisioning/scripts/bump-revision.sh"
git add REVISION
git add emmc-provisioning/scripts/bump-revision.sh \
emmc-provisioning/scripts/pre-commit-revision.sh \
emmc-provisioning/host/build-cloudinit-image.sh \
emmc-provisioning/host/flash-emmc-on-connect.sh \
emmc-provisioning/host/run-shrink-on-host.sh \
emmc-provisioning/host/README.md \
emmc-provisioning/host/cm4-flash-trigger.sh \
emmc-provisioning/dashboard/app.py \
emmc-provisioning/dashboard/templates/index.html \
emmc-provisioning/dashboard/templates/home.html \
emmc-provisioning/dashboard/templates/cloudinit_build.html \
emmc-provisioning/dashboard/templates/admin.html \
emmc-provisioning/dashboard/templates/login.html \
emmc-provisioning/dashboard/templates/portal_files.html \
emmc-provisioning/cloud-init/first-boot.sh \
emmc-provisioning/cloud-init/first-boot.conf \
emmc-provisioning/cloud-init/start-chromium.sh \
emmc-provisioning/cloud-init/01-set-rotation-once.sh \
emmc-provisioning/cloud-init/02-set-wallpaper-once.sh \
emmc-provisioning/cloud-init/set-rotation-at-login.sh \
emmc-provisioning/cloud-init/fix-reterminal-display.sh \
emmc-provisioning/scripts/deploy-to-proxmox.sh \
emmc-provisioning/scripts/deploy-dashboard-to-lxc.sh \
emmc-provisioning/scripts/sync-portal-files-to-lxc.sh \
README.md \
emmc-provisioning/README.md
exit 0

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Revision: 2
# Sync portal (file server) content from the repo to the LXC. # Sync portal (file server) content from the repo to the LXC.
# Updates /var/lib/cm4-provisioning/portal-files/ so first-boot and the # Updates /var/lib/cm4-provisioning/portal-files/ so first-boot and the
# dashboard /files/ serve the same scripts and assets as in the repo. # dashboard /files/ serve the same scripts and assets as in the repo.