feat: Enhance Task Runner with simulation and failure policy support
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Added tests for output projection and failure policy population in TaskPackPlanner.
- Introduced new failure policy manifest in TestManifests.
- Implemented simulation endpoints in the web service for task execution.
- Created TaskRunnerServiceOptions for configuration management.
- Updated appsettings.json to include TaskRunner configuration.
- Enhanced PackRunWorkerService to handle execution graphs and state management.
- Added support for parallel execution and conditional steps in the worker service.
- Updated documentation to reflect new features and changes in execution flow.
This commit is contained in:
master
2025-11-04 19:05:50 +02:00
parent 2eb6852d34
commit 3bd0955202
83 changed files with 15161 additions and 10678 deletions

View File

@@ -149,6 +149,49 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
{
builder["sbom_version_count"] = sbom.VersionTimeline.Count.ToString(CultureInfo.InvariantCulture);
builder["sbom_dependency_path_count"] = sbom.DependencyPaths.Count.ToString(CultureInfo.InvariantCulture);
if (!sbom.EnvironmentFlags.IsEmpty)
{
foreach (var flag in sbom.EnvironmentFlags.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder[$"sbom_env_{flag.Key}"] = flag.Value;
}
}
if (sbom.BlastRadius is not null)
{
builder["sbom_blast_impacted_assets"] = sbom.BlastRadius.ImpactedAssets.ToString(CultureInfo.InvariantCulture);
builder["sbom_blast_impacted_workloads"] = sbom.BlastRadius.ImpactedWorkloads.ToString(CultureInfo.InvariantCulture);
builder["sbom_blast_impacted_namespaces"] = sbom.BlastRadius.ImpactedNamespaces.ToString(CultureInfo.InvariantCulture);
if (sbom.BlastRadius.ImpactedPercentage is not null)
{
builder["sbom_blast_impacted_percentage"] = sbom.BlastRadius.ImpactedPercentage.Value.ToString("G", CultureInfo.InvariantCulture);
}
if (!sbom.BlastRadius.Metadata.IsEmpty)
{
foreach (var kvp in sbom.BlastRadius.Metadata.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder[$"sbom_blast_meta_{kvp.Key}"] = kvp.Value;
}
}
}
if (!sbom.Metadata.IsEmpty)
{
foreach (var kvp in sbom.Metadata.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder[$"sbom_meta_{kvp.Key}"] = kvp.Value;
}
}
}
if (dependency is not null)
{
foreach (var kvp in dependency.Metadata.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder[$"dependency_{kvp.Key}"] = kvp.Value;
}
}
return builder.ToImmutable();
@@ -201,12 +244,100 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
{
builder.Append("|sbom:timeline=").Append(sbom.VersionTimeline.Count);
builder.Append("|sbom:paths=").Append(sbom.DependencyPaths.Count);
foreach (var kvp in sbom.Metadata.OrderBy(k => k.Key, StringComparer.Ordinal))
foreach (var entry in sbom.VersionTimeline
.OrderBy(e => e.Version, StringComparer.Ordinal)
.ThenBy(e => e.FirstObserved.ToUnixTimeMilliseconds())
.ThenBy(e => e.LastObserved?.ToUnixTimeMilliseconds() ?? long.MinValue)
.ThenBy(e => e.Status, StringComparer.Ordinal)
.ThenBy(e => e.Source, StringComparer.Ordinal))
{
builder.Append("|sbommeta:")
.Append(kvp.Key)
.Append('=')
.Append(kvp.Value);
builder.Append("|timeline:")
.Append(entry.Version)
.Append('@')
.Append(entry.FirstObserved.ToUnixTimeMilliseconds())
.Append('@')
.Append(entry.LastObserved?.ToUnixTimeMilliseconds() ?? -1)
.Append('@')
.Append(entry.Status)
.Append('@')
.Append(entry.Source);
}
foreach (var path in sbom.DependencyPaths
.OrderBy(path => path.IsRuntime)
.ThenBy(path => string.Join(">", path.Nodes.Select(node => node.Identifier)), StringComparer.Ordinal))
{
builder.Append("|path:")
.Append(path.IsRuntime ? 'R' : 'D');
foreach (var node in path.Nodes)
{
builder.Append(":")
.Append(node.Identifier)
.Append('@')
.Append(node.Version ?? string.Empty);
}
if (!string.IsNullOrWhiteSpace(path.Source))
{
builder.Append("|pathsrc:").Append(path.Source);
}
if (!path.Metadata.IsEmpty)
{
foreach (var kvp in path.Metadata.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder.Append("|pathmeta:")
.Append(kvp.Key)
.Append('=')
.Append(kvp.Value);
}
}
}
if (!sbom.EnvironmentFlags.IsEmpty)
{
foreach (var flag in sbom.EnvironmentFlags.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder.Append("|env:")
.Append(flag.Key)
.Append('=')
.Append(flag.Value);
}
}
if (sbom.BlastRadius is not null)
{
builder.Append("|blast:")
.Append(sbom.BlastRadius.ImpactedAssets)
.Append(',')
.Append(sbom.BlastRadius.ImpactedWorkloads)
.Append(',')
.Append(sbom.BlastRadius.ImpactedNamespaces)
.Append(',')
.Append(sbom.BlastRadius.ImpactedPercentage?.ToString("G", CultureInfo.InvariantCulture) ?? string.Empty);
if (!sbom.BlastRadius.Metadata.IsEmpty)
{
foreach (var kvp in sbom.BlastRadius.Metadata.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder.Append("|blastmeta:")
.Append(kvp.Key)
.Append('=')
.Append(kvp.Value);
}
}
}
if (!sbom.Metadata.IsEmpty)
{
foreach (var kvp in sbom.Metadata.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder.Append("|sbommeta:")
.Append(kvp.Key)
.Append('=')
.Append(kvp.Value);
}
}
}
@@ -220,7 +351,20 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
.Append(':')
.Append(node.RuntimeOccurrences)
.Append(':')
.Append(node.DevelopmentOccurrences);
.Append(node.DevelopmentOccurrences)
.Append(':')
.Append(string.Join(',', node.Versions));
}
if (!dependency.Metadata.IsEmpty)
{
foreach (var kvp in dependency.Metadata.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
builder.Append("|depmeta:")
.Append(kvp.Key)
.Append('=')
.Append(kvp.Value);
}
}
}

View File

@@ -2,6 +2,7 @@ using System.Collections.Immutable;
using StellaOps.AdvisoryAI.Abstractions;
using StellaOps.AdvisoryAI.Documents;
using StellaOps.AdvisoryAI.Context;
using StellaOps.AdvisoryAI.Documents;
using StellaOps.AdvisoryAI.Tools;
namespace StellaOps.AdvisoryAI.Orchestration;