All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 4m47s
Found by reading the unencrypted VM disk after run #7: 1. Online branding never ran: Apply-Branding.ps1 had a UTF-8 em-dash in a Write-Warning STRING; Windows PowerShell 5.1 (SetupComplete) reads .ps1 as ANSI, mangled it, broke the string terminator -> whole script failed to parse -> lock/login/wallpaper branding never re-applied. Fix: ASCII-ify the em-dash AND save the branding scripts UTF-8-with-BOM so PS5.1 always decodes them correctly (verified parses under PS5.1 + PS7). 2. sm-bootstrap never removed: TearDownAsync used schtasks /tr with an inline -EncodedCommand, which silently fails past the ~261-char /tr limit, so the cleanup task was never created (confirmed NO_TASK on disk). Fix: Register-ScheduledTask (no length limit). 3. Done step: show a QR code of the BitLocker recovery key (QRCoder) for phone backup, and lay key+QR side-by-side so the Restart button no longer overflows below the fold. Verified: welcome solution builds, 29/29 tests; branding Pester 6/6 unit (offline-integration needs elevation, runs in CI). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
2.3 KiB
Plaintext
65 lines
2.3 KiB
Plaintext
@using QRCoder
|
|
@inject SilverOS.Welcome.Core.Apply.IProcessRunner ProcessRunner
|
|
|
|
<div class="step done-step">
|
|
<h1>All Done!</h1>
|
|
<p class="step-subtitle">Your SilverMetal device is configured and ready.</p>
|
|
|
|
@if (!string.IsNullOrWhiteSpace(_recoveryKey))
|
|
{
|
|
<div class="recovery-panel">
|
|
<h3>⚠ Back up your BitLocker recovery key</h3>
|
|
<p class="recovery-lead">
|
|
This is the <strong>only</strong> way back into your drive if you forget your PIN.
|
|
Scan the code with your phone, or copy the key — keep it somewhere safe and
|
|
separate from this device.
|
|
</p>
|
|
<div class="recovery-row">
|
|
@if (_qrDataUri is not null)
|
|
{
|
|
<img class="recovery-qr" src="@_qrDataUri" alt="BitLocker recovery key QR code" />
|
|
}
|
|
<pre class="recovery-key">@_recoveryKey</pre>
|
|
</div>
|
|
<p class="recovery-note"><small>
|
|
A copy is saved on this device at <code>C:\ProgramData\SilverMetal\bitlocker-recovery.txt</code>
|
|
— you can delete it once you've backed the key up elsewhere.
|
|
</small></p>
|
|
</div>
|
|
}
|
|
|
|
<button class="btn-primary btn-restart" @onclick="RestartNow">Restart Now</button>
|
|
</div>
|
|
|
|
@code {
|
|
private string? _recoveryKey;
|
|
private string? _qrDataUri;
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
try
|
|
{
|
|
const string path = @"C:\ProgramData\SilverMetal\bitlocker-recovery.txt";
|
|
if (File.Exists(path)) _recoveryKey = File.ReadAllText(path).Trim();
|
|
}
|
|
catch { /* best-effort display */ }
|
|
|
|
if (!string.IsNullOrWhiteSpace(_recoveryKey))
|
|
{
|
|
try
|
|
{
|
|
using var gen = new QRCodeGenerator();
|
|
using var data = gen.CreateQrCode(_recoveryKey, QRCodeGenerator.ECCLevel.M);
|
|
var png = new PngByteQRCode(data).GetGraphic(6);
|
|
_qrDataUri = "data:image/png;base64," + Convert.ToBase64String(png);
|
|
}
|
|
catch { /* QR is best-effort; the key text still shows */ }
|
|
}
|
|
}
|
|
|
|
private async Task RestartNow()
|
|
{
|
|
await ProcessRunner.RunAsync("cmd.exe", "/c shutdown /r /t 5", CancellationToken.None);
|
|
}
|
|
}
|