Restructure solution layout by module
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using StellaOps.TaskRunner.Core.Execution;
|
||||
using StellaOps.TaskRunner.Core.Planning;
|
||||
|
||||
namespace StellaOps.TaskRunner.Tests;
|
||||
|
||||
public sealed class PackRunApprovalCoordinatorTests
|
||||
{
|
||||
[Fact]
|
||||
public void Create_FromPlan_PopulatesApprovals()
|
||||
{
|
||||
var plan = BuildPlan();
|
||||
var coordinator = PackRunApprovalCoordinator.Create(plan, DateTimeOffset.UtcNow);
|
||||
|
||||
var approvals = coordinator.GetApprovals();
|
||||
Assert.Single(approvals);
|
||||
Assert.Equal("security-review", approvals[0].ApprovalId);
|
||||
Assert.Equal(PackRunApprovalStatus.Pending, approvals[0].Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Approve_AllowsResumeWhenLastApprovalCompletes()
|
||||
{
|
||||
var plan = BuildPlan();
|
||||
var coordinator = PackRunApprovalCoordinator.Create(plan, DateTimeOffset.UtcNow);
|
||||
|
||||
var result = coordinator.Approve("security-review", "approver-1", DateTimeOffset.UtcNow);
|
||||
|
||||
Assert.True(result.ShouldResumeRun);
|
||||
Assert.Equal(PackRunApprovalStatus.Approved, result.State.Status);
|
||||
Assert.Equal("approver-1", result.State.ActorId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reject_DoesNotResumeAndMarksState()
|
||||
{
|
||||
var plan = BuildPlan();
|
||||
var coordinator = PackRunApprovalCoordinator.Create(plan, DateTimeOffset.UtcNow);
|
||||
|
||||
var result = coordinator.Reject("security-review", "approver-1", DateTimeOffset.UtcNow, "Not safe");
|
||||
|
||||
Assert.False(result.ShouldResumeRun);
|
||||
Assert.Equal(PackRunApprovalStatus.Rejected, result.State.Status);
|
||||
Assert.Equal("Not safe", result.State.Summary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildNotifications_UsesRequirements()
|
||||
{
|
||||
var plan = BuildPlan();
|
||||
var coordinator = PackRunApprovalCoordinator.Create(plan, DateTimeOffset.UtcNow);
|
||||
|
||||
var notifications = coordinator.BuildNotifications(plan);
|
||||
Assert.Single(notifications);
|
||||
var notification = notifications[0];
|
||||
Assert.Equal("security-review", notification.ApprovalId);
|
||||
Assert.Contains("Packs.Approve", notification.RequiredGrants);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildPolicyNotifications_ProducesGateMetadata()
|
||||
{
|
||||
var plan = BuildPolicyPlan();
|
||||
var coordinator = PackRunApprovalCoordinator.Create(plan, DateTimeOffset.UtcNow);
|
||||
|
||||
var notifications = coordinator.BuildPolicyNotifications(plan);
|
||||
Assert.Single(notifications);
|
||||
var hint = notifications[0];
|
||||
Assert.Equal("policy-check", hint.StepId);
|
||||
var parameter = hint.Parameters.Single(p => p.Name == "threshold");
|
||||
Assert.False(parameter.RequiresRuntimeValue);
|
||||
var runtimeParam = hint.Parameters.Single(p => p.Name == "evidenceRef");
|
||||
Assert.True(runtimeParam.RequiresRuntimeValue);
|
||||
Assert.Equal("steps.prepare.outputs.evidence", runtimeParam.Expression);
|
||||
}
|
||||
|
||||
private static TaskPackPlan BuildPlan()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Sample);
|
||||
var planner = new TaskPackPlanner();
|
||||
var inputs = new Dictionary<string, JsonNode?>
|
||||
{
|
||||
["dryRun"] = JsonValue.Create(false)
|
||||
};
|
||||
|
||||
return planner.Plan(manifest, inputs).Plan!;
|
||||
}
|
||||
|
||||
private static TaskPackPlan BuildPolicyPlan()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.PolicyGate);
|
||||
var planner = new TaskPackPlanner();
|
||||
return planner.Plan(manifest).Plan!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.TaskRunner.Core.Execution;
|
||||
using StellaOps.TaskRunner.Core.Planning;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace StellaOps.TaskRunner.Tests;
|
||||
|
||||
public sealed class PackRunProcessorTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ProcessNewRunAsync_PersistsApprovalsAndPublishesNotifications()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Sample);
|
||||
var planner = new TaskPackPlanner();
|
||||
var plan = planner.Plan(manifest, new Dictionary<string, JsonNode?> { ["dryRun"] = JsonValue.Create(false) }).Plan!;
|
||||
var context = new PackRunExecutionContext("run-123", plan, DateTimeOffset.UtcNow);
|
||||
|
||||
var store = new TestApprovalStore();
|
||||
var publisher = new TestNotificationPublisher();
|
||||
var processor = new PackRunProcessor(store, publisher, NullLogger<PackRunProcessor>.Instance);
|
||||
|
||||
var result = await processor.ProcessNewRunAsync(context, CancellationToken.None);
|
||||
|
||||
Assert.False(result.ShouldResumeImmediately);
|
||||
var saved = Assert.Single(store.Saved);
|
||||
Assert.Equal("security-review", saved.ApprovalId);
|
||||
Assert.Single(publisher.Approvals);
|
||||
Assert.Empty(publisher.Policies);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessNewRunAsync_NoApprovals_ResumesImmediately()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Output);
|
||||
var planner = new TaskPackPlanner();
|
||||
var plan = planner.Plan(manifest).Plan!;
|
||||
var context = new PackRunExecutionContext("run-456", plan, DateTimeOffset.UtcNow);
|
||||
|
||||
var store = new TestApprovalStore();
|
||||
var publisher = new TestNotificationPublisher();
|
||||
var processor = new PackRunProcessor(store, publisher, NullLogger<PackRunProcessor>.Instance);
|
||||
|
||||
var result = await processor.ProcessNewRunAsync(context, CancellationToken.None);
|
||||
|
||||
Assert.True(result.ShouldResumeImmediately);
|
||||
Assert.Empty(store.Saved);
|
||||
Assert.Empty(publisher.Approvals);
|
||||
}
|
||||
|
||||
private sealed class TestApprovalStore : IPackRunApprovalStore
|
||||
{
|
||||
public List<PackRunApprovalState> Saved { get; } = new();
|
||||
|
||||
public Task<IReadOnlyList<PackRunApprovalState>> GetAsync(string runId, CancellationToken cancellationToken)
|
||||
=> Task.FromResult((IReadOnlyList<PackRunApprovalState>)Saved);
|
||||
|
||||
public Task SaveAsync(string runId, IReadOnlyList<PackRunApprovalState> approvals, CancellationToken cancellationToken)
|
||||
{
|
||||
Saved.Clear();
|
||||
Saved.AddRange(approvals);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task UpdateAsync(string runId, PackRunApprovalState approval, CancellationToken cancellationToken)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
|
||||
private sealed class TestNotificationPublisher : IPackRunNotificationPublisher
|
||||
{
|
||||
public List<ApprovalNotification> Approvals { get; } = new();
|
||||
public List<PolicyGateNotification> Policies { get; } = new();
|
||||
|
||||
public Task PublishApprovalRequestedAsync(string runId, ApprovalNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
Approvals.Add(notification);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task PublishPolicyGatePendingAsync(string runId, PolicyGateNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
Policies.Add(notification);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" ?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<OutputType>Exe</OutputType>
|
||||
|
||||
|
||||
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
|
||||
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
|
||||
<UseConcelierTestInfra>false</UseConcelierTestInfra>
|
||||
|
||||
|
||||
<LangVersion>preview</LangVersion>
|
||||
|
||||
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
|
||||
|
||||
|
||||
|
||||
|
||||
<PackageReference Include="xunit.v3" Version="3.0.0"/>
|
||||
|
||||
|
||||
|
||||
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<Using Include="Xunit"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<ProjectReference Include="..\StellaOps.TaskRunner.Core\StellaOps.TaskRunner.Core.csproj"/>
|
||||
|
||||
|
||||
|
||||
|
||||
<ProjectReference Include="..\StellaOps.TaskRunner.Infrastructure\StellaOps.TaskRunner.Infrastructure.csproj"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,177 @@
|
||||
using System.Linq;
|
||||
using System.Text.Json.Nodes;
|
||||
using StellaOps.TaskRunner.Core.Planning;
|
||||
|
||||
namespace StellaOps.TaskRunner.Tests;
|
||||
|
||||
public sealed class TaskPackPlannerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Plan_WithSequentialSteps_ComputesDeterministicHash()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Sample);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var inputs = new Dictionary<string, JsonNode?>
|
||||
{
|
||||
["dryRun"] = JsonValue.Create(false)
|
||||
};
|
||||
|
||||
var resultA = planner.Plan(manifest, inputs);
|
||||
Assert.True(resultA.Success);
|
||||
var plan = resultA.Plan!;
|
||||
Assert.Equal(3, plan.Steps.Count);
|
||||
Assert.Equal("plan-step", plan.Steps[0].Id);
|
||||
Assert.Equal("plan-step", plan.Steps[0].TemplateId);
|
||||
Assert.Equal("run", plan.Steps[0].Type);
|
||||
Assert.Equal("gate.approval", plan.Steps[1].Type);
|
||||
Assert.Equal("security-review", plan.Steps[1].ApprovalId);
|
||||
Assert.Equal("run", plan.Steps[2].Type);
|
||||
Assert.True(plan.Steps[2].Enabled);
|
||||
Assert.Single(plan.Approvals);
|
||||
Assert.Equal("security-review", plan.Approvals[0].Id);
|
||||
Assert.False(string.IsNullOrWhiteSpace(plan.Hash));
|
||||
|
||||
var resultB = planner.Plan(manifest, inputs);
|
||||
Assert.True(resultB.Success);
|
||||
Assert.Equal(plan.Hash, resultB.Plan!.Hash);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_WhenConditionEvaluatesFalse_DisablesStep()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Sample);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var inputs = new Dictionary<string, JsonNode?>
|
||||
{
|
||||
["dryRun"] = JsonValue.Create(true)
|
||||
};
|
||||
|
||||
var result = planner.Plan(manifest, inputs);
|
||||
Assert.True(result.Success);
|
||||
Assert.False(result.Plan!.Steps[2].Enabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_WithStepReferences_MarksParametersAsRuntime()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.StepReference);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var result = planner.Plan(manifest);
|
||||
Assert.True(result.Success);
|
||||
var plan = result.Plan!;
|
||||
Assert.Equal(2, plan.Steps.Count);
|
||||
var referenceParameters = plan.Steps[1].Parameters!;
|
||||
Assert.True(referenceParameters["sourceSummary"].RequiresRuntimeValue);
|
||||
Assert.Equal("steps.prepare.outputs.summary", referenceParameters["sourceSummary"].Expression);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_WithMapStep_ExpandsIterations()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Map);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var inputs = new Dictionary<string, JsonNode?>
|
||||
{
|
||||
["targets"] = new JsonArray("alpha", "beta", "gamma")
|
||||
};
|
||||
|
||||
var result = planner.Plan(manifest, inputs);
|
||||
Assert.True(result.Success);
|
||||
var plan = result.Plan!;
|
||||
var mapStep = plan.Steps.Single(s => s.Type == "map");
|
||||
Assert.Equal(3, mapStep.Children!.Count);
|
||||
Assert.All(mapStep.Children!, child => Assert.Equal("echo-step", child.TemplateId));
|
||||
Assert.Equal(3, mapStep.Parameters!["iterationCount"].Value!.GetValue<int>());
|
||||
Assert.Equal("alpha", mapStep.Children![0].Parameters!["item"].Value!.GetValue<string>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectApprovalRequirements_GroupsGates()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Sample);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var plan = planner.Plan(manifest).Plan!;
|
||||
var requirements = TaskPackPlanInsights.CollectApprovalRequirements(plan);
|
||||
Assert.Single(requirements);
|
||||
var requirement = requirements[0];
|
||||
Assert.Equal("security-review", requirement.ApprovalId);
|
||||
Assert.Contains("Packs.Approve", requirement.Grants);
|
||||
Assert.Equal(plan.Steps[1].Id, requirement.StepIds.Single());
|
||||
|
||||
var notifications = TaskPackPlanInsights.CollectNotificationHints(plan);
|
||||
Assert.Contains(notifications, hint => hint.Type == "approval-request" && hint.StepId == plan.Steps[1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_WithSecretReference_RecordsSecretMetadata()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Secret);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var result = planner.Plan(manifest);
|
||||
Assert.True(result.Success);
|
||||
var plan = result.Plan!;
|
||||
Assert.Single(plan.Secrets);
|
||||
Assert.Equal("apiKey", plan.Secrets[0].Name);
|
||||
var param = plan.Steps[0].Parameters!["token"];
|
||||
Assert.True(param.RequiresRuntimeValue);
|
||||
Assert.Equal("secrets.apiKey", param.Expression);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_WithOutputs_ProjectsResolvedValues()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.Output);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var result = planner.Plan(manifest);
|
||||
Assert.True(result.Success);
|
||||
var plan = result.Plan!;
|
||||
Assert.Equal(2, plan.Outputs.Count);
|
||||
|
||||
var bundle = plan.Outputs.First(o => o.Name == "bundlePath");
|
||||
Assert.NotNull(bundle.Path);
|
||||
Assert.False(bundle.Path!.RequiresRuntimeValue);
|
||||
Assert.Equal("artifacts/report.txt", bundle.Path.Value!.GetValue<string>());
|
||||
|
||||
var evidence = plan.Outputs.First(o => o.Name == "evidenceModel");
|
||||
Assert.NotNull(evidence.Expression);
|
||||
Assert.True(evidence.Expression!.RequiresRuntimeValue);
|
||||
Assert.Equal("steps.generate.outputs.evidence", evidence.Expression.Expression);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PolicyGateHints_IncludeRuntimeMetadata()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.PolicyGate);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var plan = planner.Plan(manifest).Plan!;
|
||||
var hints = TaskPackPlanInsights.CollectPolicyGateHints(plan);
|
||||
Assert.Single(hints);
|
||||
var hint = hints[0];
|
||||
Assert.Equal("policy-check", hint.StepId);
|
||||
var threshold = hint.Parameters.Single(p => p.Name == "threshold");
|
||||
Assert.False(threshold.RequiresRuntimeValue);
|
||||
Assert.Null(threshold.Expression);
|
||||
var evidence = hint.Parameters.Single(p => p.Name == "evidenceRef");
|
||||
Assert.True(evidence.RequiresRuntimeValue);
|
||||
Assert.Equal("steps.prepare.outputs.evidence", evidence.Expression);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plan_WhenRequiredInputMissing_ReturnsError()
|
||||
{
|
||||
var manifest = TestManifests.Load(TestManifests.RequiredInput);
|
||||
var planner = new TaskPackPlanner();
|
||||
|
||||
var result = planner.Plan(manifest);
|
||||
Assert.False(result.Success);
|
||||
Assert.Contains(result.Errors, error => error.Path == "inputs.sbomBundle");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using StellaOps.TaskRunner.Core.TaskPacks;
|
||||
|
||||
namespace StellaOps.TaskRunner.Tests;
|
||||
|
||||
internal static class TestManifests
|
||||
{
|
||||
public static TaskPackManifest Load(string yaml)
|
||||
{
|
||||
var loader = new TaskPackManifestLoader();
|
||||
return loader.Deserialize(yaml);
|
||||
}
|
||||
|
||||
public const string Sample = """
|
||||
apiVersion: stellaops.io/pack.v1
|
||||
kind: TaskPack
|
||||
metadata:
|
||||
name: sample-pack
|
||||
version: 1.0.0
|
||||
description: Sample pack for planner tests
|
||||
tags: [tests]
|
||||
spec:
|
||||
inputs:
|
||||
- name: dryRun
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
approvals:
|
||||
- id: security-review
|
||||
grants: ["Packs.Approve"]
|
||||
steps:
|
||||
- id: plan-step
|
||||
name: Plan
|
||||
run:
|
||||
uses: builtin:plan
|
||||
with:
|
||||
dryRun: "{{ inputs.dryRun }}"
|
||||
- id: approval
|
||||
gate:
|
||||
approval:
|
||||
id: security-review
|
||||
message: "Security approval required."
|
||||
- id: apply-step
|
||||
when: "{{ not inputs.dryRun }}"
|
||||
run:
|
||||
uses: builtin:apply
|
||||
""";
|
||||
|
||||
public const string RequiredInput = """
|
||||
apiVersion: stellaops.io/pack.v1
|
||||
kind: TaskPack
|
||||
metadata:
|
||||
name: required-input-pack
|
||||
version: 1.2.3
|
||||
spec:
|
||||
inputs:
|
||||
- name: sbomBundle
|
||||
type: object
|
||||
required: true
|
||||
steps:
|
||||
- id: noop
|
||||
run:
|
||||
uses: builtin:noop
|
||||
""";
|
||||
|
||||
public const string StepReference = """
|
||||
apiVersion: stellaops.io/pack.v1
|
||||
kind: TaskPack
|
||||
metadata:
|
||||
name: step-ref-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
steps:
|
||||
- id: prepare
|
||||
run:
|
||||
uses: builtin:prepare
|
||||
- id: consume
|
||||
run:
|
||||
uses: builtin:consume
|
||||
with:
|
||||
sourceSummary: "{{ steps.prepare.outputs.summary }}"
|
||||
""";
|
||||
|
||||
public const string Map = """
|
||||
apiVersion: stellaops.io/pack.v1
|
||||
kind: TaskPack
|
||||
metadata:
|
||||
name: map-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
inputs:
|
||||
- name: targets
|
||||
type: array
|
||||
required: true
|
||||
steps:
|
||||
- id: maintenance-loop
|
||||
map:
|
||||
items: "{{ inputs.targets }}"
|
||||
step:
|
||||
id: echo-step
|
||||
run:
|
||||
uses: builtin:echo
|
||||
with:
|
||||
target: "{{ item }}"
|
||||
""";
|
||||
|
||||
public const string Secret = """
|
||||
apiVersion: stellaops.io/pack.v1
|
||||
kind: TaskPack
|
||||
metadata:
|
||||
name: secret-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
secrets:
|
||||
- name: apiKey
|
||||
scope: Packs.Run
|
||||
description: API authentication token
|
||||
steps:
|
||||
- id: use-secret
|
||||
run:
|
||||
uses: builtin:http
|
||||
with:
|
||||
token: "{{ secrets.apiKey }}"
|
||||
""";
|
||||
|
||||
public const string Output = """
|
||||
apiVersion: stellaops.io/pack.v1
|
||||
kind: TaskPack
|
||||
metadata:
|
||||
name: output-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
steps:
|
||||
- id: generate
|
||||
run:
|
||||
uses: builtin:generate
|
||||
outputs:
|
||||
- name: bundlePath
|
||||
type: file
|
||||
path: artifacts/report.txt
|
||||
- name: evidenceModel
|
||||
type: object
|
||||
expression: "{{ steps.generate.outputs.evidence }}"
|
||||
""";
|
||||
|
||||
public const string PolicyGate = """
|
||||
apiVersion: stellaops.io/pack.v1
|
||||
kind: TaskPack
|
||||
metadata:
|
||||
name: policy-gate-pack
|
||||
version: 1.0.0
|
||||
spec:
|
||||
steps:
|
||||
- id: prepare
|
||||
run:
|
||||
uses: builtin:prepare
|
||||
- id: policy-check
|
||||
gate:
|
||||
policy:
|
||||
policy: security-hold
|
||||
parameters:
|
||||
threshold: high
|
||||
evidenceRef: "{{ steps.prepare.outputs.evidence }}"
|
||||
""";
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
|
||||
}
|
||||
Reference in New Issue
Block a user