Process snapshot tokens (#3135)

* Added Snapshot TemplateToken to AgentJobRequestMessage

* WIP for processing the snapshot token

* Changed snapshot post job step condition to Success, added comments

* Refactored snapshot post-job step

* Added evaluation of snapshot token to retrieve image name

* Added snapshot to workflow schema

* Fixed linter error

* Migrated snapshot logic to new SnapshotOperationProvider

* Fixed linter error

* Fixed linter errors

* Fixed linter error

* Fixed linter errors

* Updated L0 tests

* Fixed linter errors

* Added new JobExtensionL0 tests for snapshot post-job step

* Added JobExtensionL0 test case for snapshot mappings

* Added SnapshotOperationProviderL0 tests

* Enabled nullable types for SnapshotOperationProvider and its tests

* Added more assertions to SnapshotOperationProviderL0 tests

* Fixed linter errors

* Made sure TestHostContexts are disposed of properlyh in SnapshotOperationProviderL0 tests

* Resolved PR comments

* Fixed formatting

* Removed redundant reference

* Addressed PR comments
This commit is contained in:
David Omid
2024-02-15 02:34:52 +00:00
committed by GitHub
parent 72559572f6
commit 927b26a364
17 changed files with 344 additions and 24 deletions

View File

@@ -43,6 +43,7 @@ namespace GitHub.DistributedTask.Pipelines
TemplateToken jobOutputs,
IList<TemplateToken> defaults,
ActionsEnvironmentReference actionsEnvironment,
TemplateToken snapshot,
String messageType = JobRequestMessageTypes.PipelineAgentJobRequest)
{
this.MessageType = messageType;
@@ -57,6 +58,7 @@ namespace GitHub.DistributedTask.Pipelines
this.Workspace = workspaceOptions;
this.JobOutputs = jobOutputs;
this.ActionsEnvironment = actionsEnvironment;
this.Snapshot = snapshot;
m_variables = new Dictionary<String, VariableValue>(variables, StringComparer.OrdinalIgnoreCase);
m_maskHints = new List<MaskHint>(maskHints);
m_steps = new List<JobStep>(steps);
@@ -237,6 +239,13 @@ namespace GitHub.DistributedTask.Pipelines
set;
}
[DataMember(EmitDefaultValue = false)]
public TemplateToken Snapshot
{
get;
set;
}
/// <summary>
/// Gets the collection of variables associated with the current context.
/// </summary>

View File

@@ -29,6 +29,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
public const String Id = "id";
public const String If = "if";
public const String Image = "image";
public const String ImageName = "image-name";
public const String Include = "include";
public const String Inputs = "inputs";
public const String Job = "job";
@@ -60,6 +61,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
public const String Services = "services";
public const String Shell = "shell";
public const String Skipped = "skipped";
public const String Snapshot = "snapshot";
public const String StepEnv = "step-env";
public const String StepIfResult = "step-if-result";
public const String StepWith = "step-with";

View File

@@ -346,6 +346,39 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
return result;
}
internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token)
{
string imageName = null;
if (token is StringToken snapshotStringLiteral)
{
imageName = snapshotStringLiteral.Value;
}
else
{
var snapshotMapping = token.AssertMapping($"{PipelineTemplateConstants.Snapshot}");
foreach (var snapshotPropertyPair in snapshotMapping)
{
var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key");
switch (propertyName.Value)
{
case PipelineTemplateConstants.ImageName:
imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value;
break;
default:
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key");
break;
}
}
}
if (String.IsNullOrEmpty(imageName))
{
return null;
}
return new Snapshot(imageName);
}
private static ActionStep ConvertToStep(
TemplateContext context,
TemplateToken stepsItem,

View File

@@ -370,6 +370,32 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
return result;
}
public Snapshot EvaluateJobSnapshotRequest(TemplateToken token,
DictionaryContextData contextData,
IList<IFunctionInfo> expressionFunctions)
{
var result = default(Snapshot);
if (token != null && token.Type != TokenType.Null)
{
var context = CreateContext(contextData, expressionFunctions);
try
{
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.Snapshot, token, 0, null, omitHeader: true);
context.Errors.Check();
result = PipelineTemplateConverter.ConvertToJobSnapshotRequest(context, token);
}
catch (Exception ex) when (!(ex is TemplateValidationException))
{
context.Errors.Add(ex);
}
context.Errors.Check();
}
return result;
}
private TemplateContext CreateContext(
DictionaryContextData contextData,
IList<IFunctionInfo> expressionFunctions,

View File

@@ -0,0 +1,17 @@
using System;
using System.Runtime.Serialization;
namespace GitHub.DistributedTask.Pipelines
{
[DataContract]
public class Snapshot
{
public Snapshot(string imageName)
{
ImageName = imageName;
}
[DataMember(EmitDefaultValue = false)]
public String ImageName { get; set; }
}
}

View File

@@ -71,7 +71,8 @@
"env": "job-env",
"outputs": "job-outputs",
"defaults": "job-defaults",
"steps": "steps"
"steps": "steps",
"snapshot": "snapshot"
}
}
},
@@ -155,6 +156,24 @@
}
},
"snapshot": {
"one-of": [
"non-empty-string",
"snapshot-mapping"
]
},
"snapshot-mapping": {
"mapping": {
"properties": {
"image-name": {
"type": "non-empty-string",
"required": true
}
}
}
},
"runs-on": {
"context": [
"github",