Skip to content

Commit d384a69

Browse files
Kabuki94claude
andcommitted
Substrate fixes for MiOS-DEV (WSLg) GUI + non-interactive pipeline
WSL2 substrate etc/profile.d/mios-wslg.sh: prefer /run/user/$UID over the WSLg 9p runtime-dir (which fails sticky-bit chmod and crashes rootless podman), and export DBUS_SESSION_BUS_ADDRESS so libportal/dconf/xdg-desktop- portal find the user bus when pam_systemd is bypassed by `wsl -u root -> su -`. usr/lib/systemd/system/mios-wsl-runtime-dir.service: also symlink /mnt/wslg/wayland-0 into /run/user/$UID so Wayland clients reach the WSLg compositor through the real runtime dir. Theme + portal coverage usr/lib/systemd/user/mios-wsl-theme-bridge.service + usr/libexec/mios/wsl-theme-bridge.sh + usr/lib/systemd/user-preset/ 90-mios.preset: poll Windows AppsUseLightTheme registry, write org.gnome.desktop.interface color-scheme + gtk-theme so GTK apps follow host light/dark. usr/share/mios/mios.toml [packages.gnome-flatpak-runtime]: add dconf, gsettings-desktop-schemas, adwaita-icon-theme, gnome-themes- extra, gnome-keyring -- previously pulled transitively by gnome-shell, which the dev_overlay drops, leaving GTK in the GTK2-era fallback. Pipeline + entry-point scripts mios-pipeline.ps1 (newly tracked): centralize admin elevation -- one UAC prompt for the whole chain (with -Wait so exit codes propagate), hard-fail with a clear message under non-interactive parents instead of silently exit 0 via UAC orphan. Pass MIOS_AUTOINSTALL=1 to install .ps1 in -NoPrompt so its credential prompts don't block. build-mios.ps1: $Version pre-init before first Write-Phase ($Version was used in a string interpolation under StrictMode before its assignment); $(if ...) instead of (if ...) for subexpression in Format-Masked; honor MIOS_PIPELINE_ELEVATED to skip self-elevation fork; -Wait on the legacy standalone path. install.ps1: format-string precedence wrap-in-parens; non-interactive dashboard fallback (probes [Console]::CursorTop, falls back to plain Write-Host); $script:DashInteractive declared up front for StrictMode; AutoInstall now skips username + GHCR token prompts (was only password + hostname); honor MIOS_PIPELINE_ELEVATED. Defensive build Containerfile: CRLF -> LF strip on the writable build context before any script sources -- bullet-proof against OneDrive/Windows-tainted line endings regardless of source. Drop-in consolidation automation/41-mios-dropin-fanout.sh + usr/share/mios/dropins/{virt- gate,bare-metal-only,mios-virt-gate,mios-wsl2}.conf: single canonical source per condition gate, fanned out to 60 unit drop-ins at image build time. Replaces 60 byte-identical .conf files spread across *.service.d/ / *.socket.d/ / *.mount.d/ / *.target.d/ -- editing one gate now means editing one file. .gitignore: admit usr/lib/systemd/user/ and user-preset/ for MiOS- shipped user units (mios-wsl-theme-bridge); admit /mios-pipeline.ps1 at root. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 1fb40a5 commit d384a69

72 files changed

Lines changed: 728 additions & 206 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
!/Get-MiOS.ps1
7070
!/install.ps1
7171
!/build-mios.ps1
72+
!/mios-pipeline.ps1
7273
!/mios-build-local.ps1
7374
!/preflight.ps1
7475
!/preflight.sh
@@ -333,20 +334,40 @@ usr/lib/pam.d/*
333334
usr/lib/sysctl.d/*
334335
!/usr/lib/sysctl.d/*mios*
335336

336-
# systemd -- only MiOS-managed subdirs; blocks user/, user-generators/, timesyncd.conf etc.
337+
# systemd -- only MiOS-managed subdirs; blocks user-generators/, timesyncd.conf etc.
338+
# user/ and user-preset/ are admitted for MiOS-shipped user services
339+
# (ConditionVirtualization-gated bridges that need the per-user dbus
340+
# session, like mios-wsl-theme-bridge); the *mios* filter below keeps
341+
# distro defaults out the same way it does for system-preset.
337342
!/usr/lib/systemd/
338343
usr/lib/systemd/*
339344
!/usr/lib/systemd/journald.conf.d/
340345
!/usr/lib/systemd/journald.conf.d/**
341346
!/usr/lib/systemd/system/
342347
!/usr/lib/systemd/system-preset/
348+
!/usr/lib/systemd/user/
349+
!/usr/lib/systemd/user-preset/
343350
!/usr/lib/systemd/zram-generator.conf.d/
344351
!/usr/lib/systemd/zram-generator.conf.d/**
345352

346353
# systemd/system-preset -- *mios* only; blocks 90-default.preset, 90-systemd.preset etc.
347354
usr/lib/systemd/system-preset/*
348355
!/usr/lib/systemd/system-preset/*mios*
349356

357+
# systemd/user -- MiOS-shipped user services only; blocks dbus.service,
358+
# pipewire.service, etc. that the distro packages own.
359+
usr/lib/systemd/user/*
360+
!/usr/lib/systemd/user/mios-*.service
361+
!/usr/lib/systemd/user/mios-*.timer
362+
!/usr/lib/systemd/user/mios-*.target
363+
!/usr/lib/systemd/user/mios-*.path
364+
!/usr/lib/systemd/user/mios-*.socket
365+
366+
# systemd/user-preset -- *mios* only; blocks distro defaults like
367+
# 90-systemd-user.preset.
368+
usr/lib/systemd/user-preset/*
369+
!/usr/lib/systemd/user-preset/*mios*
370+
350371
# systemd/system -- MiOS units and drop-in dirs only; blocks sshd.service, NetworkManager.service etc.
351372
usr/lib/systemd/system/*
352373
!/usr/lib/systemd/system/mios-*.service

Containerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,24 @@ RUN --mount=type=bind,from=ctx,source=/ctx,target=/ctx,ro \
5151
set -ex; \
5252
install -d -m 0755 /tmp/build; \
5353
cp -a /ctx/automation /ctx/usr /ctx/etc /ctx/PACKAGES.md /ctx/VERSION /ctx/bib-configs /ctx/tools /tmp/build/; \
54+
# Defensive CRLF -> LF normalization. .gitattributes already pins
55+
# *.sh / *.toml / *.conf / *.yaml / *.json / *.md to LF, but Windows
56+
# build hosts (OneDrive sync in particular) bypass git's filter and
57+
# leak CRLF into the working tree. A single \r in a #!/bin/bash file
58+
# produces "$'\r': command not found" the moment bash sources it,
59+
# which surfaces as opaque exit-127 build failures hundreds of lines
60+
# later. Strip CRs from every text file in the writable build context
61+
# before any script runs -- cheap, idempotent, and immune to where
62+
# the leak originated. Binary files are skipped via grep -Iq. \
63+
find /tmp/build -type f \
64+
\( -name "*.sh" -o -name "*.toml" -o -name "*.conf" \
65+
-o -name "*.yaml" -o -name "*.yml" -o -name "*.json" \
66+
-o -name "*.md" -o -name "*.service" -o -name "*.socket" \
67+
-o -name "*.timer" -o -name "*.target" -o -name "*.preset" \
68+
-o -name "*.container" -o -name "*.image" -o -name "*.kube" \
69+
-o -name "*.volume" -o -name "*.repo" -o -name "*.policy" \
70+
-o -name "*.rules" \) \
71+
-exec sed -i 's/\r$//' {} +; \
5472
export PACKAGES_MD=/tmp/build/PACKAGES.md; \
5573
bash /tmp/build/automation/lib/packages.sh >/dev/null 2>&1 || true; \
5674
source /tmp/build/automation/lib/packages.sh; \
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/bash
2+
# automation/41-mios-dropin-fanout.sh
3+
#
4+
# Fans canonical drop-in content from /usr/share/mios/dropins/ into the
5+
# per-unit *.d/ directories at image build time.
6+
#
7+
# Why a script instead of committing 60+ identical files (or symlinks):
8+
# systemd's drop-in mechanism is per-unit -- there is no top-level
9+
# "apply this drop-in to many services" facility. The MiOS image
10+
# historically shipped 60+ byte-identical .conf files spread across
11+
# *.service.d/ / *.socket.d/ / *.mount.d/ / *.target.d/ for the four
12+
# common condition gates (virt-gate, bare-metal-only, mios-virt-gate,
13+
# mios-wsl2). Editing one gate meant editing 14+ files.
14+
#
15+
# Symlinks would deduplicate at the filesystem layer, but creating
16+
# them on Windows authoring hosts requires SeCreateSymbolicLink-
17+
# Privilege or Developer Mode (Git Bash falls back to file-copy
18+
# without them); neither is reliable in a typical author setup.
19+
#
20+
# So: store one canonical .conf per gate under usr/share/mios/dropins/
21+
# (committed, edited as a single source of truth), and fan it out at
22+
# image build time via this script. The deployed image carries the
23+
# fanned-out files exactly as before -- systemd reads them through its
24+
# normal *.d/ scanner -- but the source tree stays small and edits
25+
# propagate cleanly without filesystem-symlink dependencies.
26+
#
27+
# How to add a new gate:
28+
# 1. Drop the canonical content at usr/share/mios/dropins/NAME.conf.
29+
# 2. Add an entry to GATES below mapping NAME to the units it gates.
30+
# 3. The next image build picks it up automatically.
31+
#
32+
# How to extend an existing gate to a new unit:
33+
# 1. Append the unit name (with explicit suffix: foo.service,
34+
# bar.socket, baz.mount, ...) to that gate's UNITS list.
35+
# 2. The next image build creates the drop-in.
36+
#
37+
# Idempotent: running this script repeatedly produces the same result,
38+
# overwriting any prior copy (so an edit to the canonical propagates
39+
# even on incremental rebuilds).
40+
41+
set -euo pipefail
42+
43+
# Build context root: in the Containerfile RUN that calls this script,
44+
# /tmp/build is the writable copy of the repo and the image's /usr/lib/
45+
# already exists. Read canonicals from the former, write drop-ins into
46+
# the latter (the image hasn't yet absorbed usr/share/mios/dropins/
47+
# at this point in the build).
48+
DROPIN_SRC="${CTX:-/tmp/build}/usr/share/mios/dropins"
49+
SYSTEMD_SYSTEM_DIR="/usr/lib/systemd/system"
50+
51+
# Each line: GATE_NAME:UNIT1,UNIT2,...
52+
# GATE_NAME maps to ${DROPIN_SRC}/${GATE_NAME}.conf
53+
# UNITs include the explicit suffix (.service, .socket, .mount, .target).
54+
# The drop-in lands at ${SYSTEMD_SYSTEM_DIR}/${UNIT}.d/10-${GATE_NAME}.conf
55+
GATES=(
56+
"virt-gate:mios-cdi-detect.service,mios-ceph-bootstrap.service,mios-flatpak-install.service,mios-gpu-amd.service,mios-gpu-intel.service,mios-gpu-nvidia.service,mios-gpu-status.service,mios-grd-setup.service,mios-k3s-init.service,mios-libvirtd-setup.service,mios-nvidia-cdi.service,mios-role.service,mios-selinux-init.service,mios-waydroid-init.service"
57+
58+
"bare-metal-only:corosync.service,crowdsec.service,crowdsec-firewall-bouncer.service,mios-ha-bootstrap.service,multipathd.service,nfs-server.service,nmb.service,nvidia-powerd.service,osbuild-composer.service,osbuild-worker@1.service,pacemaker.service,pcsd.service,smb.service"
59+
60+
"mios-virt-gate:audit-rules.service,auditd.service,bootloader-update.service,ceph-bootstrap.service,chronyd.service,coreos-populate-lvmdevices.service,coreos-printk-quiet.service,dev-binderfs.mount,fapolicyd.service,firewalld.service,gdm.service,nvidia-powerd.service,tuned.service,usbguard.service,waydroid-container.service"
61+
62+
"mios-wsl2:avahi-daemon.service,avahi-daemon.socket,boot.mount,boot-complete.target,cloud-config.service,cloud-init-local.service,cloud-init-network.service,greenboot-healthcheck.service,qemu-guest-agent.service,rpm-ostree-fix-shadow-mode.service,stratisd.service,systemd-homed.service,systemd-logind.service,var-lib-nfs-rpc_pipefs.mount,virtlxcd.service,virtlxcd-admin.socket,virtlxcd-ro.socket,zincati.service"
63+
)
64+
65+
count=0
66+
for entry in "${GATES[@]}"; do
67+
gate_name="${entry%%:*}"
68+
units_csv="${entry#*:}"
69+
src="${DROPIN_SRC}/${gate_name}.conf"
70+
71+
if [[ ! -f "$src" ]]; then
72+
echo "[mios-dropin-fanout] FATAL: canonical missing at $src" >&2
73+
exit 1
74+
fi
75+
76+
IFS=',' read -ra units <<< "$units_csv"
77+
for unit in "${units[@]}"; do
78+
dropin_dir="${SYSTEMD_SYSTEM_DIR}/${unit}.d"
79+
dropin_file="${dropin_dir}/10-${gate_name}.conf"
80+
install -d -m 0755 "$dropin_dir"
81+
install -m 0644 "$src" "$dropin_file"
82+
count=$((count + 1))
83+
done
84+
done
85+
86+
echo "[mios-dropin-fanout] fanned out $count drop-ins from ${#GATES[@]} canonical gates"

build-mios.ps1

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ function Invoke-BIBRun {
240240
$bibOp = if ($candidate.Length -gt 80) { $candidate.Substring(0, 80) + '...' } else { $candidate }
241241
} elseif (-not [string]::IsNullOrWhiteSpace($stripped)) {
242242
$candidate = ($stripped -replace '\s+', ' ').Trim()
243-
$bibOp = Format-Masked (if ($candidate.Length -gt 80) { $candidate.Substring(0, 80) + '...' } else { $candidate })
243+
$bibOp = Format-Masked $(if ($candidate.Length -gt 80) { $candidate.Substring(0, 80) + '...' } else { $candidate })
244244
}
245245
Write-Progress -Activity " $Label" -Id 1 -ParentId 0 `
246246
-Status "Lines: $bibN" -CurrentOperation $bibOp `
@@ -251,10 +251,17 @@ function Invoke-BIBRun {
251251
}
252252

253253
# --- Auto-Elevation ---
254-
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
255-
Write-Host " Relaunching as Administrator..." -ForegroundColor Cyan
256-
Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs
257-
return
254+
# mios-pipeline.ps1 elevates the whole chain once and sets
255+
# MIOS_PIPELINE_ELEVATED=1; trust that and skip the self-elevation
256+
# fork (which historically broke non-interactive parents -- the
257+
# elevated child became an orphan UAC window, the un-elevated copy
258+
# returned 0, and the pipeline thought the build had succeeded).
259+
if (-not $env:MIOS_PIPELINE_ELEVATED) {
260+
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
261+
Write-Host " Relaunching as Administrator..." -ForegroundColor Cyan
262+
Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs -Wait
263+
return
264+
}
258265
}
259266

260267
# -- Self-Build defaults (initialized early - referenced throughout) --
@@ -265,6 +272,13 @@ Set-StrictMode -Version Latest
265272
# ==============================================================================
266273
# CONFIGURATION
267274
# ==============================================================================
275+
# Resolve $Version up front -- Write-Phase formats its progress label with
276+
# "${Version}", and StrictMode ($ErrorActionPreference=Stop) treats an
277+
# unset variable in a string interpolation as fatal. The first Write-Phase
278+
# call lives in the .env.mios block immediately below, so this assignment
279+
# has to happen before that, not after the .env import.
280+
$v = Get-Content "VERSION" -ErrorAction SilentlyContinue; $Version = if ($v) { $v.Trim() } else { "v0.2.4" }
281+
268282
# Source .env.mios if present
269283
if (Test-Path ".env.mios") {
270284
Write-Phase "0.1" "Loading Unified Environment"
@@ -277,7 +291,6 @@ if (Test-Path ".env.mios") {
277291
}
278292
}
279293

280-
$v = Get-Content "VERSION" -ErrorAction SilentlyContinue; $Version = if ($v) { $v.Trim() } else { "v0.2.4" }
281294
$ImageName = if ($env:MIOS_IMAGE_NAME) { ($env:MIOS_IMAGE_NAME -split '/')[-1] -replace ':.*$','' } else { "mios" }
282295
$ImageTag = "latest"
283296
$MIOS_USER_ADMIN = "mios" # @track:USER_ADMIN

etc/profile.d/mios-wslg.sh

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,41 @@
1717

1818
[ -d /mnt/wslg ] || return 0
1919

20-
# Wayland socket: WSLg publishes wayland-0 inside /mnt/wslg/ which itself
21-
# becomes XDG_RUNTIME_DIR's source. Some flatpak runtimes also need
22-
# WAYLAND_DISPLAY pointing at the bare socket name.
23-
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/mnt/wslg/runtime-dir}"
20+
# XDG_RUNTIME_DIR: WSLg's default of /mnt/wslg/runtime-dir is a 9p mount
21+
# from the Windows host and does not support sticky-bit chmod. Rootless
22+
# podman creates $XDG_RUNTIME_DIR/libpod with the sticky bit and crashes
23+
# with "set sticky bit on: chmod ... operation not permitted" if pointed
24+
# there. mios-wsl-runtime-dir.service pre-creates /run/user/$UID on a
25+
# real tmpfs; prefer it when present, fall back to the WSLg dir only if
26+
# the service hasn't run (early-boot interactive shells).
27+
if [ -d "/run/user/$(id -u)" ]; then
28+
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
29+
# If we landed in /mnt/wslg/runtime-dir from an outer login that
30+
# exported it before this script ran, swap it out -- podman will
31+
# crash otherwise.
32+
case "$XDG_RUNTIME_DIR" in
33+
/mnt/wslg/*) export XDG_RUNTIME_DIR="/run/user/$(id -u)" ;;
34+
esac
35+
else
36+
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/mnt/wslg/runtime-dir}"
37+
fi
2438
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
2539
export DISPLAY="${DISPLAY:-:0}"
2640
export PULSE_SERVER="${PULSE_SERVER:-/mnt/wslg/PulseServer}"
2741

42+
# DBUS_SESSION_BUS_ADDRESS: pam_systemd would normally set this to the
43+
# user@$UID.service bus socket on a real login. WSL's `wsl -u root` ->
44+
# `su - mios` chain bypasses PAM so the var is unset. libportal then
45+
# tries to autolaunch a session bus via the X11 cookie, which fails with
46+
# "Cannot autolaunch D-Bus without X11 $DISPLAY", taking down xdg-desktop-
47+
# portal (and dconf-service, which is dbus-activated through the same
48+
# bus) for every flatpak / GTK app. mios-wsl-runtime-dir.service starts
49+
# user@$UID.service so the bus socket exists -- this just exports the
50+
# address so apps can find it.
51+
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ] && [ -S "/run/user/$(id -u)/bus" ]; then
52+
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
53+
fi
54+
2855
# WSLg sets WSL_INTEROP via /init for the outer ns; the nested ns may
2956
# lose it. Re-derive from /run/WSL/<pid>_interop if present so
3057
# explorer.exe / wslpath etc. still work from the nested shell.

install.ps1

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,14 @@ function Format-Masked {
8686
}
8787

8888
# ─── dashboard state ──────────────────────────────────────────────────────────
89-
$script:DashRow = 0
90-
$script:DashH = 0
91-
$script:DashReady = $false
89+
$script:DashRow = 0
90+
$script:DashH = 0
91+
$script:DashReady = $false
92+
# Resolved on first Show-Dashboard call: $true if [Console] has a real
93+
# console with usable cursor positioning, $false if we're running under
94+
# captured stdout (background pipeline invocation). $null means "probe
95+
# hasn't run yet".
96+
$script:DashInteractive = $null
9297
$script:BuildStart = [DateTime]::Now
9398
$script:ErrCount = 0
9499
$script:WarnCount = 0
@@ -174,7 +179,7 @@ function Show-Dashboard {
174179
default { "[ ] " }
175180
}
176181
$tCell = if ($ph.ElapsedS -gt 0) { "{0:D2}:{1:D2}" -f [int]($ph.ElapsedS/60), ($ph.ElapsedS%60) } else { " " }
177-
$lines.Add("| {0,3} {1} {2} {3} |" -f $ph.Id, $stateStr, (_dpad $ph.Name 48), $tCell)
182+
$lines.Add(("| {0,3} {1} {2} {3} |" -f $ph.Id, $stateStr, (_dpad $ph.Name 48), $tCell))
178183
}
179184

180185
$lines.Add($(_dsep '-'))
@@ -184,6 +189,33 @@ function Show-Dashboard {
184189

185190
$script:DashH = $lines.Count
186191

192+
# Probe once whether [Console] is a real console with a usable cursor.
193+
# Background pipeline invocations (mios-pipeline.ps1 -> install.ps1
194+
# via Start-Process redirected stdout) have no console handle and
195+
# CursorTop throws "The handle is invalid." Detect that up front and
196+
# fall back to plain line writes -- the dashboard becomes a snapshot
197+
# log instead of an in-place TUI, which is the right shape for
198+
# captured output anyway.
199+
if ($null -eq $script:DashInteractive) {
200+
try {
201+
$null = [Console]::CursorTop
202+
$script:DashInteractive = $true
203+
} catch {
204+
$script:DashInteractive = $false
205+
}
206+
}
207+
208+
if (-not $script:DashInteractive) {
209+
# Non-interactive: emit on first call only (full state) then
210+
# just the changed Op line via Set-Op. Avoids spamming 28 lines
211+
# for every progress tick.
212+
if (-not $script:DashReady) {
213+
foreach ($l in $lines) { Write-Host $l }
214+
$script:DashReady = $true
215+
}
216+
return
217+
}
218+
187219
if (-not $script:DashReady) {
188220
# First render -- write fresh, record position
189221
$script:DashRow = [Console]::CursorTop
@@ -336,11 +368,18 @@ function Invoke-BIBRun {
336368
}
337369

338370
# ─── elevation ────────────────────────────────────────────────────────────────
339-
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
340-
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
341-
Write-Host " Relaunching as Administrator..." -ForegroundColor Cyan
342-
Start-Process pwsh.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs
343-
return
371+
# Trust mios-pipeline.ps1's centralized elevation; only self-elevate
372+
# when invoked standalone (the legacy operator path). Skipping when
373+
# MIOS_PIPELINE_ELEVATED=1 prevents the previous failure mode where the
374+
# pipeline's elevated session would re-fork and orphan another UAC
375+
# window on this script's entry.
376+
if (-not $env:MIOS_PIPELINE_ELEVATED) {
377+
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
378+
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
379+
Write-Host " Relaunching as Administrator..." -ForegroundColor Cyan
380+
Start-Process pwsh.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs -Wait
381+
return
382+
}
344383
}
345384

346385
# ─── static header (printed once, scrolls away) ───────────────────────────────
@@ -484,9 +523,9 @@ Finish-Phase 5
484523
Start-Phase 6 "Collecting credentials..."
485524
$AutoInstall = $env:MIOS_AUTOINSTALL -eq "1"
486525

487-
$U = if ($env:MIOS_USER) { $env:MIOS_USER } else {
488-
Read-Plain "Admin username:" "mios"
489-
}
526+
$U = if ($env:MIOS_USER) { $env:MIOS_USER }
527+
elseif ($AutoInstall) { 'mios' }
528+
else { Read-Plain "Admin username:" "mios" }
490529
$P = if ($env:MIOS_PASSWORD) { $env:MIOS_PASSWORD } else {
491530
if ($AutoInstall) { "mios" } else {
492531
$pw1 = Read-Masked "Admin password:" ""
@@ -508,11 +547,15 @@ if ($HostIn -eq "mios") {
508547
$HostIn = "mios-$('{0:D5}' -f (Get-Random -Min 10000 -Max 99999))"
509548
}
510549

511-
$GhcrToken = if ($env:GHCR_TOKEN) { $env:GHCR_TOKEN } else {
512-
$t = Read-Masked "GitHub PAT for ghcr.io base image pull (github.com/settings/tokens):"
513-
$t
514-
}
515-
Register-Secret $GhcrToken
550+
# GHCR token: only needed for pulling the private MiOS helper image as
551+
# the build base on the *first* build of a new host. AutoInstall (which
552+
# pipeline -NoPrompt sets) skips the prompt; the build then falls back
553+
# to alpine/python helpers, which is the same path a fresh first-time
554+
# build takes anyway.
555+
$GhcrToken = if ($env:GHCR_TOKEN) { $env:GHCR_TOKEN }
556+
elseif ($AutoInstall) { '' }
557+
else { Read-Masked "GitHub PAT for ghcr.io base image pull (github.com/settings/tokens):" }
558+
if ($GhcrToken) { Register-Secret $GhcrToken }
516559

517560
$RegUser = if ($env:MIOS_GHCR_USER) { $env:MIOS_GHCR_USER } else { "MiOS-DEV" }
518561
$GhcrImage = "ghcr.io/$RegUser/${ImageName}:${ImageTag}"

0 commit comments

Comments
 (0)