From 3eb16e7f628270067d31ad0fd1328a9b85b23e82 Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:22:07 -0400 Subject: [PATCH] Post-merge polish: README enroll quick-start, promote.sh, flake.lock bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README.md: - Add enroll quick-start (4 commands) at the top - Add steady-state operations table (doctor.sh, promote.sh, sync status) - Add repository layout reference scripts/promote.sh: - Promotes sourceos-builder-aarch64 content view through dev → candidate → stable in the local Katello instance - Supports --version, --to, --dry-run, --org flags - Reads Katello password from /etc/sourceos/katello-admin-password or FOREMAN_PASSWORD env var - After promotion, sourceos-syncd picks up the new stable version within the next poll interval flake.lock: - Bump sourceos-syncd-src to include sync status command (PR #38) --- README.md | 68 +++++++++++++++++++++---- flake.lock | 6 +-- scripts/promote.sh | 122 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 12 deletions(-) create mode 100755 scripts/promote.sh diff --git a/README.md b/README.md index 6317bc1..0c5f99f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,68 @@ # source-os -This is the main SociOS Linux SourceOS repository. +NixOS realization root for the SourceOS Linux control-plane stack. -## Role +## Enroll an M2 -`source-os` is the Linux realization home for the SourceOS control-plane stack. It carries host roles, profiles, images, builders, and Linux-facing integration surfaces that realize the AgentPlane contract on Linux hosts. +```sh +# Phase A — install Asahi Linux (see docs/bootstrap/M2_ENROLL.md) +curl https://alx.sh | sh -## Current surfaces +# Phase B — replace Fedora with NixOS +curl -L https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | \ + NIX_CHANNEL=nixos-unstable NO_REBOOT=1 bash +git clone https://github.com/SociOS-Linux/source-os.git /opt/sourceos/source-os +reboot -- `docs/repository-layout.md` — repository shape and intent -- `docs/agentplane-integration.md` — contract boundary with AgentPlane and shared standards -- `docs/mesh/` — mesh Linux estate integration planning, path-template mapping, and staged workstreams -- `linux/` — concrete Linux-facing templates for systemd-networkd, NetworkManager, nftables, and helper units -- `profiles/` / `modules/` — Nix realization surfaces +# Phase C — enroll (run as root from the repo root, ~35 min) +sudo bash scripts/enroll.sh + +# Verify +bash scripts/doctor.sh +``` + +Full runbook: [`docs/bootstrap/M2_ENROLL.md`](docs/bootstrap/M2_ENROLL.md) + +## What enrollment gives you + +| Component | What it does | +|-----------|-------------| +| `sourceos-syncd` daemon | Polls local Katello every 5 min; applies NixOS updates; emits `SyncCycleReceipt` | +| `sourceos-boot` rollback | Auto-rolls back if post-boot health check fails | +| `harmonia` | Local Nix binary cache served at `http://127.0.0.1:8101` | +| Foreman+Katello | Local content lifecycle manager (Docker, linux/amd64 via qemu) | +| SOPS secrets | Katello password encrypted with device age key; never committed | + +## Day-2 operations + +```sh +# Check full stack health +bash scripts/doctor.sh + +# Promote a new build to stable (triggers daemon sync within 5 min) +bash scripts/promote.sh --version + +# Daemon status +sourceos-syncd sync status + +# Last sync receipt +sourceos-syncd receipts last + +# Live daemon logs +journalctl -u sourceos-syncd -f +``` + +## Repository layout + +- `hosts/builder-aarch64/` — M2 Asahi NixOS host config +- `modules/nixos/sourceos-syncd/` — NixOS module for the sync daemon +- `packages/sourceos-syncd/` / `packages/sourceos-boot/` — Nix derivations +- `scripts/enroll.sh` — one-shot M2 enrollment +- `scripts/doctor.sh` — full stack health check +- `scripts/promote.sh` — promote Katello content view to stable +- `scripts/katello-sourceos-setup.sh` — idempotent Katello org/product setup +- `docs/bootstrap/M2_ENROLL.md` — detailed enrollment runbook +- `profiles/` / `modules/` — shared NixOS profiles and modules ## Boundary rule diff --git a/flake.lock b/flake.lock index 0633fcc..66cec8a 100644 --- a/flake.lock +++ b/flake.lock @@ -117,11 +117,11 @@ "sourceos-syncd-src": { "flake": false, "locked": { - "lastModified": 1781627880, - "narHash": "sha256-9QRlZyhKNTH15KWf+M5P137Axf43FHq6AMExAKauMHs=", + "lastModified": 1781630368, + "narHash": "sha256-z3tQB35G6oh+rKLV9hJtk0c3LtTDgVwSYDWy/6WtE2Y=", "owner": "SourceOS-Linux", "repo": "sourceos-syncd", - "rev": "8f845b7d3053ba704a4c570aa23e9fae9565914c", + "rev": "a5762779dacc371bf35ce522b4cd3b0b7952a9ed", "type": "github" }, "original": { diff --git a/scripts/promote.sh b/scripts/promote.sh new file mode 100755 index 0000000..255ac94 --- /dev/null +++ b/scripts/promote.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash +# Promote the latest (or a specific) sourceos-builder-aarch64 content view +# version through dev → candidate → stable in the local Katello instance. +# +# Usage: +# bash scripts/promote.sh # promote latest +# bash scripts/promote.sh --version 1.3 # promote specific version +# bash scripts/promote.sh --to stable # promote only to stable +# bash scripts/promote.sh --dry-run # print plan, do nothing +# +# After promotion, sourceos-syncd will detect the new stable version +# within SOURCEOS_POLL_INTERVAL seconds (default 300) and apply the update. + +set -euo pipefail + +KATELLO_URL="${FOREMAN_URL:-https://127.0.0.1:8443}" +KATELLO_USER="${FOREMAN_USER:-admin}" +ORG="${SOURCEOS_ORG:-SocioProphet}" +CV_NAME="${SOURCEOS_CV:-sourceos-builder-aarch64}" +TARGET_ENVS=("dev" "candidate" "stable") +CV_VERSION="" +DRY_RUN=0 +KATELLO_PW_FILE="${SOURCEOS_DIR:-/etc/sourceos}/katello-admin-password" + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' + +ok() { printf " ${GREEN}✓${NC} %s\n" "$*"; } +info() { printf " ${CYAN}·${NC} %s\n" "$*"; } +warn() { printf " ${YELLOW}!${NC} %s\n" "$*"; } +die() { printf " ${RED}✗ ERROR:${NC} %s\n" "$*" >&2; exit 1; } + +usage() { + sed -n 's/^# //p' "$0" | head -12 + exit 0 +} + +# ── Args ────────────────────────────────────────────────────────────────────── + +while [[ $# -gt 0 ]]; do + case "$1" in + --version) CV_VERSION="$2"; shift 2 ;; + --to) TARGET_ENVS=("$2"); shift 2 ;; + --dry-run) DRY_RUN=1; shift ;; + --katello-url) KATELLO_URL="$2"; shift 2 ;; + --org) ORG="$2"; shift 2 ;; + --help|-h) usage ;; + *) die "Unknown argument: $1" ;; + esac +done + +# ── Credentials ─────────────────────────────────────────────────────────────── + +if [[ -n "${FOREMAN_PASSWORD:-}" ]]; then + KATELLO_PASSWORD="${FOREMAN_PASSWORD}" +elif [[ -f "${KATELLO_PW_FILE}" ]]; then + KATELLO_PASSWORD=$(cat "${KATELLO_PW_FILE}") +else + die "Katello password not found. Set FOREMAN_PASSWORD or ensure ${KATELLO_PW_FILE} exists." +fi + +# ── Hammer wrapper ──────────────────────────────────────────────────────────── + +hammer() { + docker exec katello-foreman hammer \ + --server "${KATELLO_URL}" \ + --username "${KATELLO_USER}" \ + --password "${KATELLO_PASSWORD}" \ + "$@" +} + +# ── Discover version ────────────────────────────────────────────────────────── + +if [[ -z "${CV_VERSION}" ]]; then + info "Querying latest content view version..." + CV_VERSION=$(hammer --output json content-view version list \ + --organization "${ORG}" \ + --content-view "${CV_NAME}" 2>/dev/null | \ + python3 -c " +import json, sys +vs = json.load(sys.stdin) +if not vs: sys.exit(1) +print(sorted(vs, key=lambda v: v['ID'])[-1]['Version']) +") || die "Could not determine latest CV version — is Katello running? (docker ps)" +fi + +info "Content view: ${CV_NAME}" +info "Version: ${CV_VERSION}" +info "Org: ${ORG}" +info "Promoting to: ${TARGET_ENVS[*]}" +[[ $DRY_RUN -eq 1 ]] && warn "DRY RUN — no changes will be made" + +# ── Promote ─────────────────────────────────────────────────────────────────── + +echo + +for env in "${TARGET_ENVS[@]}"; do + if [[ $DRY_RUN -eq 1 ]]; then + info "[dry-run] would promote v${CV_VERSION} → ${env}" + continue + fi + + info "Promoting v${CV_VERSION} → ${env}..." + if hammer content-view version promote \ + --organization "${ORG}" \ + --content-view "${CV_NAME}" \ + --version "${CV_VERSION}" \ + --to-lifecycle-environment "${env}" 2>/dev/null; then + ok "v${CV_VERSION} → ${env}" + else + warn "Promotion to ${env} skipped (already at this version or previous env not promoted)" + fi +done + +echo + +if [[ $DRY_RUN -eq 0 ]]; then + POLL_INTERVAL="${SOURCEOS_POLL_INTERVAL:-300}" + ok "Done. sourceos-syncd will detect v${CV_VERSION} in stable within ${POLL_INTERVAL}s." + info "Force immediate check: systemctl restart sourceos-syncd" + info "Watch: journalctl -u sourceos-syncd -f" + info "Verify: sourceos-syncd sync status" +fi