Adding Snapshot additional mapping tokens (#3468)

* Adding Snapshot additional mapping tokens

* Lint failure fixes

* Lint failure fixes - 2

* Lint failure fixes - 3

* Fixed a few nits

* Lint fixes

* Removed unncessary white space
This commit is contained in:
Raj R
2024-10-01 11:04:48 -07:00
committed by GitHub
parent 3696b7d89f
commit e292ec220e
6 changed files with 103 additions and 9 deletions

View File

@@ -403,7 +403,7 @@ namespace GitHub.Runner.Worker
var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>(); var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>();
jobContext.RegisterPostJobStep(new JobExtensionRunner( jobContext.RegisterPostJobStep(new JobExtensionRunner(
runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest), runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest),
condition: $"{PipelineTemplateConstants.Success}()", condition: snapshotRequest.Condition,
displayName: $"Create custom image", displayName: $"Create custom image",
data: null)); data: null));
} }

View File

@@ -30,6 +30,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
public const String If = "if"; public const String If = "if";
public const String Image = "image"; public const String Image = "image";
public const String ImageName = "image-name"; public const String ImageName = "image-name";
public const String CustomImageVersion = "version";
public const String Include = "include"; public const String Include = "include";
public const String Inputs = "inputs"; public const String Inputs = "inputs";
public const String Job = "job"; public const String Job = "job";

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Linq; using System.Linq;
using GitHub.DistributedTask.Expressions2; using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.Expressions2.Sdk; using GitHub.DistributedTask.Expressions2.Sdk;
@@ -349,6 +350,10 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token) internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token)
{ {
string imageName = null; string imageName = null;
string version = "1.*";
string versionString = string.Empty;
var condition = $"{PipelineTemplateConstants.Success}()";
if (token is StringToken snapshotStringLiteral) if (token is StringToken snapshotStringLiteral)
{ {
imageName = snapshotStringLiteral.Value; imageName = snapshotStringLiteral.Value;
@@ -359,11 +364,19 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
foreach (var snapshotPropertyPair in snapshotMapping) foreach (var snapshotPropertyPair in snapshotMapping)
{ {
var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key"); var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key");
var propertyValue = snapshotPropertyPair.Value;
switch (propertyName.Value) switch (propertyName.Value)
{ {
case PipelineTemplateConstants.ImageName: case PipelineTemplateConstants.ImageName:
imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value; imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value;
break; break;
case PipelineTemplateConstants.If:
condition = ConvertToIfCondition(context, propertyValue, false);
break;
case PipelineTemplateConstants.CustomImageVersion:
versionString = propertyValue.AssertString($"job {PipelineTemplateConstants.Snapshot} {PipelineTemplateConstants.CustomImageVersion}").Value;
version = IsSnapshotImageVersionValid(versionString) ? versionString : null;
break;
default: default:
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key"); propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key");
break; break;
@@ -376,7 +389,26 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
return null; return null;
} }
return new Snapshot(imageName); return new Snapshot(imageName)
{
Condition = condition,
Version = version
};
}
private static bool IsSnapshotImageVersionValid(string versionString)
{
var versionSegments = versionString.Split(".");
if (versionSegments.Length != 2 ||
!versionSegments[1].Equals("*") ||
!Int32.TryParse(versionSegments[0], NumberStyles.None, CultureInfo.InvariantCulture, result: out int parsedMajor) ||
parsedMajor < 0)
{
return false;
}
return true;
} }
private static ActionStep ConvertToStep( private static ActionStep ConvertToStep(

View File

@@ -1,17 +1,27 @@
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
namespace GitHub.DistributedTask.Pipelines namespace GitHub.DistributedTask.Pipelines
{ {
[DataContract] [DataContract]
public class Snapshot public class Snapshot
{ {
public Snapshot(string imageName) public Snapshot(string imageName, string condition = null, string version = null)
{ {
ImageName = imageName; ImageName = imageName;
Condition = condition ?? $"{PipelineTemplateConstants.Success}()";
Version = version ?? "1.*";
} }
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public String ImageName { get; set; } public String ImageName { get; set; }
[DataMember(EmitDefaultValue = false)]
public String Condition { get; set; }
[DataMember(EmitDefaultValue = false)]
public String Version { get; set; }
} }
} }

View File

@@ -169,11 +169,28 @@
"image-name": { "image-name": {
"type": "non-empty-string", "type": "non-empty-string",
"required": true "required": true
},
"if": "snapshot-if",
"version": {
"type": "non-empty-string",
"required": false
} }
} }
} }
}, },
"snapshot-if": {
"context": [
"github",
"inputs",
"vars",
"needs",
"strategy",
"matrix"
],
"string": {}
},
"runs-on": { "runs-on": {
"context": [ "context": [
"github", "github",

View File

@@ -506,7 +506,27 @@ namespace GitHub.Runner.Common.Tests.Worker
return EnsureSnapshotPostJobStepForToken(mappingToken, snapshot); return EnsureSnapshotPostJobStepForToken(mappingToken, snapshot);
} }
private async Task EnsureSnapshotPostJobStepForToken(TemplateToken snapshotToken, Pipelines.Snapshot expectedSnapshot) [Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public Task EnsureSnapshotPostJobStepForMappingToken_WithIf_Is_False()
{
var snapshot = new Pipelines.Snapshot("TestImageNameFromMappingToken", condition: $"{PipelineTemplateConstants.Success}() && 1==0", version: "2.*");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
var condition = new StringToken(null, null, null, snapshot.Condition);
var version = new StringToken(null, null, null, snapshot.Version);
var mappingToken = new MappingToken(null, null, null)
{
{ new StringToken(null,null,null, PipelineTemplateConstants.ImageName), imageNameValueStringToken },
{ new StringToken(null,null,null, PipelineTemplateConstants.If), condition },
{ new StringToken(null,null,null, PipelineTemplateConstants.CustomImageVersion), version }
};
return EnsureSnapshotPostJobStepForToken(mappingToken, snapshot, skipSnapshotStep: true);
}
private async Task EnsureSnapshotPostJobStepForToken(TemplateToken snapshotToken, Pipelines.Snapshot expectedSnapshot, bool skipSnapshotStep = false)
{ {
using (TestHostContext hc = CreateTestContext()) using (TestHostContext hc = CreateTestContext())
{ {
@@ -524,14 +544,28 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal(1, postJobSteps.Count); Assert.Equal(1, postJobSteps.Count);
var snapshotStep = postJobSteps.First(); var snapshotStep = postJobSteps.First();
_jobEc.JobSteps.Enqueue(snapshotStep);
var _stepsRunner = new StepsRunner();
_stepsRunner.Initialize(hc);
await _stepsRunner.RunAsync(_jobEc);
Assert.Equal("Create custom image", snapshotStep.DisplayName); Assert.Equal("Create custom image", snapshotStep.DisplayName);
Assert.Equal($"{PipelineTemplateConstants.Success}()", snapshotStep.Condition); Assert.Equal(expectedSnapshot.Condition ?? $"{PipelineTemplateConstants.Success}()", snapshotStep.Condition);
// Run the mock snapshot step, so we can verify it was executed with the expected snapshot object. // Run the mock snapshot step, so we can verify it was executed with the expected snapshot object.
await snapshotStep.RunAsync(); // await snapshotStep.RunAsync();
if (skipSnapshotStep)
{
Assert.Null(_requestedSnapshot);
}
else
{
Assert.NotNull(_requestedSnapshot); Assert.NotNull(_requestedSnapshot);
Assert.Equal(expectedSnapshot.ImageName, _requestedSnapshot.ImageName); Assert.Equal(expectedSnapshot.ImageName, _requestedSnapshot.ImageName);
Assert.Equal(expectedSnapshot.Condition ?? $"{PipelineTemplateConstants.Success}()", _requestedSnapshot.Condition);
Assert.Equal(expectedSnapshot.Version ?? "1.*", _requestedSnapshot.Version);
}
} }
} }
} }