mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-23 20:26:10 +00:00
Agent: the bootstrap provenance also reads the log names from before 2026-08-18
The first device on 2.0.1486 reported no bootstrap log at all. Installers up to v2.3 write bootstrap_agent.log, and the MSI's own downloader (before the loader of 2026-09-08) msi-bootstrap.log; the reader knew only the names from the rename of 2026-08-18 on, so the old directly assigned installers that agent_started is meant to count read as "no bootstrap". - The installer log is the more recently written of bootstrap-script.log and bootstrap_agent.log; the MSI channel also covers msi-bootstrap.log, which has no version. - Tests: the old installer name, both installer logs (the newer one counts), the old MSI downloader. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+35
-1
@@ -12,7 +12,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Runtime
|
||||
/// The bootstrap provenance in <c>agent_started</c>. Fixture lines are the scripts' real
|
||||
/// formats: installer <c>[ts] Bootstrap script version: vX.Y</c>
|
||||
/// (Install-AutopilotMonitor.ps1), loader <c>[ts] === Autopilot Monitor bootstrap loader vX.Y
|
||||
/// started (user=..., pid=...) ===</c> (Start-AutopilotMonitor.ps1).
|
||||
/// started (user=..., pid=...) ===</c> (Start-AutopilotMonitor.ps1), the MSI's downloader before
|
||||
/// the loader <c>[ts] MSI bootstrap runner started (user=..., pid=...).</c>.
|
||||
/// </summary>
|
||||
public sealed class BootstrapProvenanceReaderTests : IDisposable
|
||||
{
|
||||
@@ -79,6 +80,39 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Runtime
|
||||
Assert.Null(p.ScriptVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_installer_from_before_the_log_rename_is_read_under_its_old_name()
|
||||
{
|
||||
Write(BootstrapProvenanceReader.LegacyInstallerLogName, InstallerRun("2.3"));
|
||||
|
||||
var p = BootstrapProvenanceReader.Read(_dir);
|
||||
|
||||
Assert.True(p.ScriptLogPresent);
|
||||
Assert.Equal("2.3", p.ScriptVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void With_both_installer_logs_the_most_recently_written_one_counts()
|
||||
{
|
||||
Write(BootstrapProvenanceReader.InstallerLogName, InstallerRun("2.6"));
|
||||
Write(BootstrapProvenanceReader.LegacyInstallerLogName, InstallerRun("2.3"));
|
||||
File.SetLastWriteTimeUtc(Path.Combine(_dir, BootstrapProvenanceReader.InstallerLogName), DateTime.UtcNow.AddDays(-3));
|
||||
File.SetLastWriteTimeUtc(Path.Combine(_dir, BootstrapProvenanceReader.LegacyInstallerLogName), DateTime.UtcNow.AddMinutes(-1));
|
||||
|
||||
Assert.Equal("2.3", BootstrapProvenanceReader.Read(_dir).ScriptVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_msi_downloader_from_before_the_loader_counts_as_msi_without_version()
|
||||
{
|
||||
Write(BootstrapProvenanceReader.LegacyMsiLoaderLogName, "[2026-08-01 09:00:00] MSI bootstrap runner started (user=SECRETUSER, pid=4242).");
|
||||
|
||||
var p = BootstrapProvenanceReader.Read(_dir);
|
||||
|
||||
Assert.Equal("msi", p.LoaderLog);
|
||||
Assert.Null(p.LoaderVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_msi_loader_log_is_told_apart_from_the_intune_loader()
|
||||
{
|
||||
|
||||
+28
-14
@@ -13,10 +13,13 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Runtime
|
||||
/// passing a version to <c>--install</c> would only ever cover installers released after that
|
||||
/// change -- the old installers still assigned in Intune are exactly the ones worth counting.
|
||||
/// <list type="bullet">
|
||||
/// <item><c>bootstrap-script.log</c>: <c>Bootstrap script version: vX.Y</c>, written by every
|
||||
/// <item><c>bootstrap-script.log</c> (<c>bootstrap_agent.log</c> up to installer v2.3, before
|
||||
/// the log rename of 2026-08-18): <c>Bootstrap script version: vX.Y</c>, written by every
|
||||
/// installer run since 2026-04-09. A log without that line means an older installer.</item>
|
||||
/// <item><c>bootstrap-loader.log</c> (Intune) / <c>bootstrap-msi.log</c> (MSI):
|
||||
/// <c>bootstrap loader vX.Y started</c>, written by every loader since v1.0.</item>
|
||||
/// <item><c>bootstrap-loader.log</c> (Intune) / <c>bootstrap-msi.log</c> (MSI,
|
||||
/// <c>msi-bootstrap.log</c> before 2026-08-18): <c>bootstrap loader vX.Y started</c>, written by
|
||||
/// every loader since v1.0. The MSI's own downloader, which the loader replaced on 2026-09-08,
|
||||
/// writes no version.</item>
|
||||
/// <item><c>oobe_bootstrap.log</c>: the OOBE go-script (generated by the backend, no version).</item>
|
||||
/// </list>
|
||||
/// Only the version number is extracted; the loader line also names the account, which is
|
||||
@@ -29,6 +32,10 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Runtime
|
||||
internal const string MsiLoaderLogName = "bootstrap-msi.log";
|
||||
internal const string OobeLogName = "oobe_bootstrap.log";
|
||||
|
||||
// Names before the log rename of 2026-08-18; copies of those scripts are still assigned.
|
||||
internal const string LegacyInstallerLogName = "bootstrap_agent.log";
|
||||
internal const string LegacyMsiLoaderLogName = "msi-bootstrap.log";
|
||||
|
||||
// The logs grow across runs; the last run's lines sit at the end. One run writes a few KB.
|
||||
private const int TailBytes = 64 * 1024;
|
||||
|
||||
@@ -42,22 +49,19 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Runtime
|
||||
{
|
||||
try
|
||||
{
|
||||
var installerLog = ReadTail(Path.Combine(logDirectory, InstallerLogName));
|
||||
var installerLog = ReadTail(MostRecentlyWritten(logDirectory, InstallerLogName, LegacyInstallerLogName));
|
||||
|
||||
// Intune loader and MSI loader write separate files; when a device has both, the
|
||||
// more recently written one belongs to the bootstrap that ran last.
|
||||
var loader = new[] { (Name: LoaderLogName, Channel: "loader"), (Name: MsiLoaderLogName, Channel: "msi") }
|
||||
.Select(l => (l.Channel, Path: Path.Combine(logDirectory, l.Name)))
|
||||
.Where(l => File.Exists(l.Path))
|
||||
.OrderByDescending(l => File.GetLastWriteTimeUtc(l.Path))
|
||||
.Select(l => (l.Channel, Version: LastVersion(ReadTail(l.Path), LoaderStartLine)))
|
||||
.FirstOrDefault();
|
||||
// Intune loader and MSI loader write separate files; every name but the Intune
|
||||
// loader's is the MSI's.
|
||||
var loaderPath = MostRecentlyWritten(logDirectory, LoaderLogName, MsiLoaderLogName, LegacyMsiLoaderLogName);
|
||||
var loaderChannel = loaderPath == null ? null
|
||||
: string.Equals(Path.GetFileName(loaderPath), LoaderLogName, StringComparison.OrdinalIgnoreCase) ? "loader" : "msi";
|
||||
|
||||
return new BootstrapProvenance(
|
||||
scriptLogPresent: installerLog != null,
|
||||
scriptVersion: LastVersion(installerLog, InstallerVersionLine),
|
||||
loaderLog: loader.Channel,
|
||||
loaderVersion: loader.Version,
|
||||
loaderLog: loaderChannel,
|
||||
loaderVersion: LastVersion(ReadTail(loaderPath), LoaderStartLine),
|
||||
oobeLogPresent: File.Exists(Path.Combine(logDirectory, OobeLogName)));
|
||||
}
|
||||
catch
|
||||
@@ -66,6 +70,16 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Runtime
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The most recently written of the named logs that exist, or null. On a device with
|
||||
/// several, it belongs to the bootstrap that ran last.
|
||||
/// </summary>
|
||||
private static string MostRecentlyWritten(string directory, params string[] names) =>
|
||||
names.Select(name => Path.Combine(directory, name))
|
||||
.Where(path => File.Exists(path))
|
||||
.OrderByDescending(path => File.GetLastWriteTimeUtc(path))
|
||||
.FirstOrDefault();
|
||||
|
||||
internal static string LastVersion(string text, Regex line)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return null;
|
||||
|
||||
Reference in New Issue
Block a user