mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
5 Commits
v2.281.0
...
users/tihu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c44b34e436 | ||
|
|
738602ee55 | ||
|
|
502b9f06f2 | ||
|
|
310530add6 | ||
|
|
de07637563 |
@@ -46,6 +46,7 @@ namespace GitHub.Runner.Common
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.SetOutputCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.SaveStateCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.AddPathCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.RefreshTokenCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.AddMaskCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.AddMatcherCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.RemoveMatcherCommandExtension, Runner.Worker");
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace GitHub.Runner.Common
|
||||
Task<List<TimelineRecord>> UpdateTimelineRecordsAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken);
|
||||
Task RaisePlanEventAsync<T>(Guid scopeIdentifier, string hubName, Guid planId, T eventData, CancellationToken cancellationToken) where T : JobEvent;
|
||||
Task<Timeline> GetTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken);
|
||||
Task<GitHubToken> RefreshGitHubTokenAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid jobId, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed class JobServer : RunnerService, IJobServer
|
||||
@@ -113,5 +114,11 @@ namespace GitHub.Runner.Common
|
||||
CheckConnection();
|
||||
return _taskClient.GetTimelineAsync(scopeIdentifier, hubName, planId, timelineId, includeRecords: true, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task<GitHubToken> RefreshGitHubTokenAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid jobId, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckConnection();
|
||||
return _taskClient.RefreshTokenAsync(scopeIdentifier, hubName, planId, jobId, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +288,30 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RefreshTokenCommandExtension : RunnerService, IActionCommandExtension
|
||||
{
|
||||
public string Command => "refresh-token";
|
||||
public bool OmitEcho => false;
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
{
|
||||
ArgUtil.NotNullOrEmpty(command.Data, "token file");
|
||||
var runnerTemp = HostContext.GetDirectory(WellKnownDirectory.Temp);
|
||||
var tempFile = Path.Combine(runnerTemp, command.Data);
|
||||
if (!tempFile.StartsWith(runnerTemp + Path.DirectorySeparatorChar))
|
||||
{
|
||||
throw new Exception($"'{command.Data}' has to be under runner temp directory '{runnerTemp}'");
|
||||
}
|
||||
|
||||
Trace.Info("Here");
|
||||
var githubToken = context.GetGitHubToken().GetAwaiter().GetResult();
|
||||
File.WriteAllText(tempFile, githubToken);
|
||||
Trace.Info("HereAgain");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AddMatcherCommandExtension : RunnerService, IActionCommandExtension
|
||||
{
|
||||
public string Command => "add-matcher";
|
||||
|
||||
@@ -21,11 +21,24 @@ using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplat
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
public class PrepareResult
|
||||
{
|
||||
public PrepareResult(List<JobExtensionRunner> containerSetupSteps, Dictionary<Guid, IActionRunner> preStepTracker)
|
||||
{
|
||||
this.ContainerSetupSteps = containerSetupSteps;
|
||||
this.PreStepTracker = preStepTracker;
|
||||
}
|
||||
|
||||
public List<JobExtensionRunner> ContainerSetupSteps { get; set; }
|
||||
|
||||
public Dictionary<Guid, IActionRunner> PreStepTracker { get; set; }
|
||||
}
|
||||
|
||||
[ServiceLocator(Default = typeof(ActionManager))]
|
||||
public interface IActionManager : IRunnerService
|
||||
{
|
||||
Dictionary<Guid, ContainerInfo> CachedActionContainers { get; }
|
||||
Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps);
|
||||
Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps);
|
||||
Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action);
|
||||
}
|
||||
|
||||
@@ -39,7 +52,7 @@ namespace GitHub.Runner.Worker
|
||||
private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>();
|
||||
|
||||
public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers;
|
||||
public async Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
|
||||
public async Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
|
||||
{
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ArgUtil.NotNull(steps, nameof(steps));
|
||||
@@ -49,6 +62,7 @@ namespace GitHub.Runner.Worker
|
||||
Dictionary<string, List<Guid>> imagesToBuild = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase);
|
||||
Dictionary<string, ActionContainer> imagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase);
|
||||
List<JobExtensionRunner> containerSetupSteps = new List<JobExtensionRunner>();
|
||||
Dictionary<Guid, IActionRunner> preStepTracker = new Dictionary<Guid, IActionRunner>();
|
||||
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
|
||||
|
||||
// TODO: Depreciate the PREVIEW_ACTION_TOKEN
|
||||
@@ -111,6 +125,21 @@ namespace GitHub.Runner.Worker
|
||||
imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo;
|
||||
}
|
||||
}
|
||||
|
||||
var repoAction = action.Reference as Pipelines.RepositoryPathReference;
|
||||
if (repoAction.RepositoryType != Pipelines.PipelineConstants.SelfAlias)
|
||||
{
|
||||
var definition = LoadAction(executionContext, action);
|
||||
if (definition.Data.Execution.HasInit)
|
||||
{
|
||||
var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||
actionRunner.Action = action;
|
||||
actionRunner.Stage = ActionRunStage.Pre;
|
||||
actionRunner.Condition = definition.Data.Execution.InitCondition;
|
||||
|
||||
preStepTracker[action.Id] = actionRunner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +176,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
#endif
|
||||
|
||||
return containerSetupSteps;
|
||||
return new PrepareResult(containerSetupSteps, preStepTracker);
|
||||
}
|
||||
|
||||
public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action)
|
||||
@@ -162,6 +191,7 @@ namespace GitHub.Runner.Worker
|
||||
Data = new ActionDefinitionData()
|
||||
};
|
||||
|
||||
// bool localAction = false;
|
||||
if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry)
|
||||
{
|
||||
Trace.Info("Load action that reference container from registry.");
|
||||
@@ -180,6 +210,7 @@ namespace GitHub.Runner.Worker
|
||||
var repoAction = action.Reference as Pipelines.RepositoryPathReference;
|
||||
if (string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// localAction = true;
|
||||
actionDirectory = executionContext.GetGitHubContext("workspace");
|
||||
if (!string.IsNullOrEmpty(repoAction.Path))
|
||||
{
|
||||
@@ -239,6 +270,11 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Info($"Action container env: {StringUtil.ConvertToJson(containerAction.Environment)}.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(containerAction.Init))
|
||||
{
|
||||
Trace.Info($"Action container init entrypoint: {containerAction.Init}.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(containerAction.EntryPoint))
|
||||
{
|
||||
Trace.Info($"Action container entrypoint: {containerAction.EntryPoint}.");
|
||||
@@ -258,6 +294,7 @@ namespace GitHub.Runner.Worker
|
||||
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.NodeJS)
|
||||
{
|
||||
var nodeAction = definition.Data.Execution as NodeJSActionExecutionData;
|
||||
Trace.Info($"Action init node.js file: {nodeAction.Init ?? "N/A"}.");
|
||||
Trace.Info($"Action node.js file: {nodeAction.Script}.");
|
||||
Trace.Info($"Action cleanup node.js file: {nodeAction.Cleanup ?? "N/A"}.");
|
||||
}
|
||||
@@ -772,6 +809,8 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.Container;
|
||||
|
||||
public override bool HasInit => !string.IsNullOrEmpty(Init);
|
||||
public override bool HasMain => !string.IsNullOrEmpty(EntryPoint);
|
||||
public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
|
||||
|
||||
public string Image { get; set; }
|
||||
@@ -782,6 +821,8 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public MappingToken Environment { get; set; }
|
||||
|
||||
public string Init { get; set; }
|
||||
|
||||
public string Cleanup { get; set; }
|
||||
}
|
||||
|
||||
@@ -789,10 +830,14 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.NodeJS;
|
||||
|
||||
public override bool HasInit => !string.IsNullOrEmpty(Init);
|
||||
public override bool HasMain => !string.IsNullOrEmpty(Script);
|
||||
public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
|
||||
|
||||
public string Script { get; set; }
|
||||
|
||||
public string Init { get; set; }
|
||||
|
||||
public string Cleanup { get; set; }
|
||||
}
|
||||
|
||||
@@ -800,6 +845,9 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.Plugin;
|
||||
|
||||
public override bool HasInit => false;
|
||||
public override bool HasMain => true;
|
||||
|
||||
public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
|
||||
|
||||
public string Plugin { get; set; }
|
||||
@@ -810,16 +858,20 @@ namespace GitHub.Runner.Worker
|
||||
public sealed class ScriptActionExecutionData : ActionExecutionData
|
||||
{
|
||||
public override ActionExecutionType ExecutionType => ActionExecutionType.Script;
|
||||
|
||||
public override bool HasInit => false;
|
||||
public override bool HasMain => true;
|
||||
public override bool HasCleanup => false;
|
||||
}
|
||||
|
||||
public abstract class ActionExecutionData
|
||||
{
|
||||
private string _initCondition = $"{Constants.Expressions.Always}()";
|
||||
private string _cleanupCondition = $"{Constants.Expressions.Always}()";
|
||||
|
||||
public abstract ActionExecutionType ExecutionType { get; }
|
||||
|
||||
public abstract bool HasInit { get; }
|
||||
public abstract bool HasMain { get; }
|
||||
public abstract bool HasCleanup { get; }
|
||||
|
||||
public string CleanupCondition
|
||||
@@ -827,6 +879,12 @@ namespace GitHub.Runner.Worker
|
||||
get { return _cleanupCondition; }
|
||||
set { _cleanupCondition = value; }
|
||||
}
|
||||
|
||||
public string InitCondition
|
||||
{
|
||||
get { return _initCondition; }
|
||||
set { _initCondition = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ContainerSetupInfo
|
||||
|
||||
@@ -280,6 +280,9 @@ namespace GitHub.Runner.Worker
|
||||
var envToken = default(MappingToken);
|
||||
var mainToken = default(StringToken);
|
||||
var pluginToken = default(StringToken);
|
||||
var preToken = default(StringToken);
|
||||
var preEntrypointToken = default(StringToken);
|
||||
var preIfToken = default(StringToken);
|
||||
var postToken = default(StringToken);
|
||||
var postEntrypointToken = default(StringToken);
|
||||
var postIfToken = default(StringToken);
|
||||
@@ -318,6 +321,15 @@ namespace GitHub.Runner.Worker
|
||||
case "post-if":
|
||||
postIfToken = run.Value.AssertString("post-if");
|
||||
break;
|
||||
case "pre":
|
||||
preToken = run.Value.AssertString("pre");
|
||||
break;
|
||||
case "pre-entrypoint":
|
||||
preEntrypointToken = run.Value.AssertString("pre-entrypoint");
|
||||
break;
|
||||
case "pre-if":
|
||||
preIfToken = run.Value.AssertString("pre-if");
|
||||
break;
|
||||
default:
|
||||
Trace.Info($"Ignore run property {runsKey}.");
|
||||
break;
|
||||
@@ -340,6 +352,8 @@ namespace GitHub.Runner.Worker
|
||||
Arguments = argsToken,
|
||||
EntryPoint = entrypointToken?.Value,
|
||||
Environment = envToken,
|
||||
Init = preEntrypointToken?.Value,
|
||||
InitCondition = preIfToken?.Value ?? "always()",
|
||||
Cleanup = postEntrypointToken?.Value,
|
||||
CleanupCondition = postIfToken?.Value ?? "always()"
|
||||
};
|
||||
@@ -356,6 +370,8 @@ namespace GitHub.Runner.Worker
|
||||
return new NodeJSActionExecutionData()
|
||||
{
|
||||
Script = mainToken.Value,
|
||||
Init = preToken?.Value,
|
||||
InitCondition = preIfToken?.Value ?? "always()",
|
||||
Cleanup = postToken?.Value,
|
||||
CleanupCondition = postIfToken?.Value ?? "always()"
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public enum ActionRunStage
|
||||
{
|
||||
Pre,
|
||||
Main,
|
||||
Post,
|
||||
}
|
||||
@@ -81,20 +82,18 @@ namespace GitHub.Runner.Worker
|
||||
ActionExecutionData handlerData = definition.Data?.Execution;
|
||||
ArgUtil.NotNull(handlerData, nameof(handlerData));
|
||||
|
||||
if (handlerData.HasInit &&
|
||||
Action.Reference is Pipelines.RepositoryPathReference repoAction &&
|
||||
string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ExecutionContext.Warning($"`pre` execution is not supported for local action from '{repoAction.Path}'");
|
||||
}
|
||||
|
||||
// The action has post cleanup defined.
|
||||
// we need to create timeline record for them and add them to the step list that StepRunner is using
|
||||
if (handlerData.HasCleanup && Stage == ActionRunStage.Main)
|
||||
if (handlerData.HasCleanup && (Stage == ActionRunStage.Pre || Stage == ActionRunStage.Main))
|
||||
{
|
||||
string postDisplayName = null;
|
||||
if (this.DisplayName.StartsWith(PipelineTemplateConstants.RunDisplayPrefix))
|
||||
{
|
||||
postDisplayName = $"Post {this.DisplayName.Substring(PipelineTemplateConstants.RunDisplayPrefix.Length)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
postDisplayName = $"Post {this.DisplayName}";
|
||||
}
|
||||
|
||||
string postDisplayName = $"Post {this.DisplayName}";
|
||||
var repositoryReference = Action.Reference as RepositoryPathReference;
|
||||
var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
|
||||
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
|
||||
@@ -108,7 +107,7 @@ namespace GitHub.Runner.Worker
|
||||
actionRunner.Condition = handlerData.CleanupCondition;
|
||||
actionRunner.DisplayName = postDisplayName;
|
||||
|
||||
ExecutionContext.RegisterPostJobStep($"{actionRunner.Action.Name}_post", actionRunner);
|
||||
ExecutionContext.RegisterPostJobStep(actionRunner);
|
||||
}
|
||||
|
||||
IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>();
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace GitHub.Runner.Worker
|
||||
data: data);
|
||||
|
||||
executionContext.Debug($"Register post job cleanup for stopping/deleting containers.");
|
||||
executionContext.RegisterPostJobStep(nameof(StopContainersAsync), postJobStep);
|
||||
executionContext.RegisterPostJobStep(postJobStep);
|
||||
|
||||
// Check whether we are inside a container.
|
||||
// Our container feature requires to map working directory from host to the container.
|
||||
|
||||
@@ -21,6 +21,7 @@ using System.Collections;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -98,7 +99,9 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// others
|
||||
void ForceTaskComplete();
|
||||
void RegisterPostJobStep(string refName, IStep step);
|
||||
void RegisterPostJobStep(IStep step);
|
||||
Task<string> GetGitHubToken();
|
||||
Task UpdateGitHubTokenInContext();
|
||||
}
|
||||
|
||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||
@@ -110,6 +113,7 @@ namespace GitHub.Runner.Worker
|
||||
private readonly object _loggerLock = new object();
|
||||
private readonly HashSet<string> _outputvariables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object _matchersLock = new object();
|
||||
public readonly List<Task> _getGitHubTokenTasks = new List<Task>();
|
||||
|
||||
private event OnMatcherChanged _onMatcherChanged;
|
||||
|
||||
@@ -130,6 +134,13 @@ namespace GitHub.Runner.Worker
|
||||
// only job level ExecutionContext will track throttling delay.
|
||||
private long _totalThrottlingDelayInMilliseconds = 0;
|
||||
|
||||
private Guid _jobId;
|
||||
private Guid _scopeIdentifier;
|
||||
private string _hubName;
|
||||
private Guid _planId;
|
||||
|
||||
private Stopwatch _githubTokenExpireTimer = new Stopwatch();
|
||||
|
||||
public Guid Id => _record.Id;
|
||||
public string ScopeName { get; private set; }
|
||||
public string ContextName { get; private set; }
|
||||
@@ -154,6 +165,9 @@ namespace GitHub.Runner.Worker
|
||||
// Only job level ExecutionContext has PostJobSteps
|
||||
public Stack<IStep> PostJobSteps { get; private set; }
|
||||
|
||||
// Only job level ExecutionContext has StepsWithPostRegisted
|
||||
public HashSet<Guid> StepsWithPostRegisted { get; private set; }
|
||||
|
||||
public bool EchoOnActionCommand { get; set; }
|
||||
|
||||
|
||||
@@ -239,9 +253,68 @@ namespace GitHub.Runner.Worker
|
||||
});
|
||||
}
|
||||
|
||||
public void RegisterPostJobStep(string refName, IStep step)
|
||||
public Task<string> GetGitHubToken()
|
||||
{
|
||||
step.ExecutionContext = Root.CreatePostChild(step.DisplayName, refName, IntraActionState);
|
||||
Trace.Info($"Try get new GITHUB_TOKEN");
|
||||
return Root.RefreshGitHubToken();
|
||||
}
|
||||
|
||||
public async Task UpdateGitHubTokenInContext()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Root.EnsureGitHubToken();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Warning($"Fail to get a new GITHUB_TOKEN, error: {ex.Message}");
|
||||
this.Debug(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> RefreshGitHubToken()
|
||||
{
|
||||
Trace.Info("Request new GITHUB_TOKEN");
|
||||
var jobServer = HostContext.GetService<IJobServer>();
|
||||
var githubToken = await jobServer.RefreshGitHubTokenAsync(_scopeIdentifier, _hubName, _planId, _jobId, CancellationToken.None);
|
||||
|
||||
if (!string.IsNullOrEmpty(githubToken?.Token))
|
||||
{
|
||||
// register secret masker
|
||||
Trace.Info("Register secret masker for new GITHUB_TOKEN");
|
||||
HostContext.SecretMasker.AddValue(githubToken.Token);
|
||||
return githubToken.Token;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Get empty GTIHUB_TOKEN.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EnsureGitHubToken()
|
||||
{
|
||||
// needs to refresh GITHUB_TOKEN every 50 mins since the token is good for 60 min by default
|
||||
if (_githubTokenExpireTimer.Elapsed.TotalMilliseconds > 10)
|
||||
{
|
||||
var githubToken = await this.RefreshGitHubToken();
|
||||
var secretsContext = ExpressionValues["secrets"] as DictionaryContextData;
|
||||
secretsContext["GITHUB_TOKEN"] = new StringContextData(githubToken);
|
||||
var githubContext = ExpressionValues["github"] as GitHubContext;
|
||||
githubContext["token"] = new StringContextData(githubToken);
|
||||
|
||||
_githubTokenExpireTimer.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterPostJobStep(IStep step)
|
||||
{
|
||||
if (step is IActionRunner actionRunner && !Root.StepsWithPostRegisted.Add(actionRunner.Action.Id))
|
||||
{
|
||||
Trace.Info($"'post' of '{actionRunner.DisplayName}' already push to post step stack.");
|
||||
return;
|
||||
}
|
||||
|
||||
step.ExecutionContext = Root.CreatePostChild(step.DisplayName, ScopeName, ContextName, IntraActionState);
|
||||
Root.PostJobSteps.Push(step);
|
||||
}
|
||||
|
||||
@@ -608,6 +681,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
ExpressionValues["env"] = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
_githubTokenExpireTimer.Start();
|
||||
|
||||
// Prepend Path
|
||||
PrependPath = new List<string>();
|
||||
@@ -618,6 +692,14 @@ namespace GitHub.Runner.Worker
|
||||
// PostJobSteps for job ExecutionContext
|
||||
PostJobSteps = new Stack<IStep>();
|
||||
|
||||
// StepsWithPostRegisted for job ExecutionContext
|
||||
StepsWithPostRegisted = new HashSet<Guid>();
|
||||
|
||||
_scopeIdentifier = message.Plan.ScopeIdentifier;
|
||||
_hubName = message.Plan.PlanType;
|
||||
_jobId = message.JobId;
|
||||
_planId = message.Plan.PlanId;
|
||||
|
||||
// Job timeline record.
|
||||
InitializeTimelineRecord(
|
||||
timelineId: message.Timeline.Id,
|
||||
@@ -818,7 +900,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
private IExecutionContext CreatePostChild(string displayName, string refName, Dictionary<string, string> intraActionState)
|
||||
private IExecutionContext CreatePostChild(string displayName, string scopeName, string contextName, Dictionary<string, string> intraActionState)
|
||||
{
|
||||
if (!_expandedForPostJob)
|
||||
{
|
||||
@@ -827,7 +909,8 @@ namespace GitHub.Runner.Worker
|
||||
_childTimelineRecordOrder = _childTimelineRecordOrder * 2;
|
||||
}
|
||||
|
||||
return CreateChild(Guid.NewGuid(), displayName, refName, null, null, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count);
|
||||
var newGuid = Guid.NewGuid();
|
||||
return CreateChild(newGuid, displayName, newGuid.ToString("N"), scopeName, contextName, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,10 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint");
|
||||
}
|
||||
}
|
||||
else if (stage == ActionRunStage.Pre)
|
||||
{
|
||||
container.ContainerEntryPoint = Data.Init;
|
||||
}
|
||||
else if (stage == ActionRunStage.Post)
|
||||
{
|
||||
container.ContainerEntryPoint = Data.Cleanup;
|
||||
|
||||
@@ -60,6 +60,10 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
target = Data.Script;
|
||||
}
|
||||
else if (stage == ActionRunStage.Pre)
|
||||
{
|
||||
target = Data.Init;
|
||||
}
|
||||
else if (stage == ActionRunStage.Post)
|
||||
{
|
||||
target = Data.Cleanup;
|
||||
|
||||
@@ -116,8 +116,8 @@ namespace GitHub.Runner.Worker
|
||||
// Download actions not already in the cache
|
||||
Trace.Info("Downloading actions");
|
||||
var actionManager = HostContext.GetService<IActionManager>();
|
||||
var prepareSteps = await actionManager.PrepareActionsAsync(context, message.Steps);
|
||||
preJobSteps.AddRange(prepareSteps);
|
||||
var prepareResult = await actionManager.PrepareActionsAsync(context, message.Steps);
|
||||
preJobSteps.AddRange(prepareResult.ContainerSetupSteps);
|
||||
|
||||
// Add start-container steps, record and stop-container steps
|
||||
if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0)
|
||||
@@ -158,9 +158,23 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
actionRunner.TryEvaluateDisplayName(contextData, context);
|
||||
jobSteps.Add(actionRunner);
|
||||
|
||||
if (prepareResult.PreStepTracker.TryGetValue(step.Id, out var preStep))
|
||||
{
|
||||
Trace.Info($"Adding pre-{action.DisplayName}.");
|
||||
preStep.TryEvaluateDisplayName(contextData, context);
|
||||
preStep.DisplayName = $"Pre {preStep.DisplayName}";
|
||||
preJobSteps.Add(preStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intraActionStates = new Dictionary<Guid, Dictionary<string, string>>();
|
||||
foreach (var preStep in prepareResult.PreStepTracker)
|
||||
{
|
||||
intraActionStates[preStep.Key] = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
// Create execution context for pre-job steps
|
||||
foreach (var step in preJobSteps)
|
||||
{
|
||||
@@ -171,6 +185,12 @@ namespace GitHub.Runner.Worker
|
||||
Guid stepId = Guid.NewGuid();
|
||||
extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N"));
|
||||
}
|
||||
else if (step is IActionRunner actionStep)
|
||||
{
|
||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||
Guid stepId = Guid.NewGuid();
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(stepId, actionStep.DisplayName, null, actionStep.Action.ScopeName, actionStep.Action.ContextName, intraActionStates[actionStep.Action.Id]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create execution context for job steps
|
||||
@@ -179,7 +199,14 @@ namespace GitHub.Runner.Worker
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName);
|
||||
if (intraActionStates.ContainsKey(actionStep.Action.Id))
|
||||
{
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName, intraActionStates[actionStep.Action.Id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Start
|
||||
step.ExecutionContext.Start();
|
||||
await step.ExecutionContext.UpdateGitHubTokenInContext();
|
||||
|
||||
// Initialize scope
|
||||
if (InitializeScope(step, scopeInputs))
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"entrypoint": "non-empty-string",
|
||||
"args": "container-runs-args",
|
||||
"env": "container-runs-env",
|
||||
"pre-entrypoint": "non-empty-string",
|
||||
"pre-if": "non-empty-string",
|
||||
"post-entrypoint": "non-empty-string",
|
||||
"post-if": "non-empty-string"
|
||||
}
|
||||
@@ -67,6 +69,8 @@
|
||||
"properties": {
|
||||
"using": "non-empty-string",
|
||||
"main": "non-empty-string",
|
||||
"pre": "non-empty-string",
|
||||
"pre-if": "non-empty-string",
|
||||
"post": "non-empty-string",
|
||||
"post-if": "non-empty-string"
|
||||
}
|
||||
|
||||
@@ -317,5 +317,40 @@ namespace GitHub.DistributedTask.WebApi
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [Preview API]
|
||||
/// </summary>
|
||||
/// <param name="scopeIdentifier">The project GUID to scope the request</param>
|
||||
/// <param name="hubName">The name of the server hub: "build" for the Build server or "rm" for the Release Management server</param>
|
||||
/// <param name="planId"></param>
|
||||
/// <param name="jobId"></param>
|
||||
/// <param name="userState"></param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
public virtual Task<GitHubToken> RefreshTokenAsync(
|
||||
Guid scopeIdentifier,
|
||||
string hubName,
|
||||
Guid planId,
|
||||
Guid jobId,
|
||||
object userState = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HttpMethod httpMethod = new HttpMethod("GET");
|
||||
Guid locationId = new Guid("8aa8aff7-751b-496e-be8d-b7818770efb3");
|
||||
object routeValues = new { scopeIdentifier = scopeIdentifier, hubName = hubName, planId = planId };
|
||||
|
||||
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
|
||||
queryParams.Add("jobId", jobId.ToString());
|
||||
|
||||
return SendAsync<GitHubToken>(
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 1),
|
||||
queryParameters: queryParams,
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,17 @@ using System.Runtime.Serialization;
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
|
||||
[DataContract]
|
||||
public class GitHubToken
|
||||
{
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string Token { get; set; }
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public DateTime Expires_at { get; set; }
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class Issue
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
//Assert
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
@@ -164,7 +164,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
Assert.True(steps.Count == 0);
|
||||
}
|
||||
@@ -203,7 +203,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfile");
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
Assert.Equal(Path.Combine(actionDir, "Dockerfile"), (steps[0].Data as ContainerSetupInfo).Container.Dockerfile);
|
||||
@@ -243,7 +243,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -282,7 +282,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -322,7 +322,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerfileRelativePath");
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -362,7 +362,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerHubImage");
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image);
|
||||
@@ -401,7 +401,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionYamlFile_DockerHubImage");
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
Assert.Equal((steps[0].Data as ContainerSetupInfo).StepIds[0], actionId);
|
||||
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image);
|
||||
@@ -440,7 +440,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithactionfileanddockerfile");
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
|
||||
@@ -557,7 +557,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
//Assert
|
||||
Assert.Equal(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
|
||||
@@ -618,7 +618,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
|
||||
// node.js based action doesn't need any extra steps to build/pull containers.
|
||||
Assert.True(steps.Count == 0);
|
||||
@@ -629,6 +629,47 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async void PrepareActions_RepositoryActionWithInvalidWrapperActionfile_Node()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
var actionId = Guid.NewGuid();
|
||||
var actions = new List<Pipelines.ActionStep>
|
||||
{
|
||||
new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action",
|
||||
Id = actionId,
|
||||
Reference = new Pipelines.RepositoryPathReference()
|
||||
{
|
||||
Name = "TingluoHuang/runner_L0",
|
||||
Ref = "RepositoryActionWithInvalidWrapperActionfile_Node",
|
||||
RepositoryType = "GitHub"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Act
|
||||
try
|
||||
{
|
||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
}
|
||||
catch (ArgumentNullException e)
|
||||
{
|
||||
Assert.Contains("Entry javascript fils is not provided.", e.Message);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
|
||||
@@ -63,6 +63,52 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_ContainerAction_Dockerfile_Pre()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_init.yml"));
|
||||
|
||||
//Assert
|
||||
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.Container, result.Execution.ExecutionType);
|
||||
|
||||
var containerAction = result.Execution as ContainerActionExecutionData;
|
||||
|
||||
Assert.Equal("Dockerfile", containerAction.Image);
|
||||
Assert.Equal("main.sh", containerAction.EntryPoint);
|
||||
Assert.Equal("init.sh", containerAction.Init);
|
||||
Assert.Equal("success()", containerAction.InitCondition);
|
||||
Assert.Equal("bzz", containerAction.Arguments[0].ToString());
|
||||
Assert.Equal("Token", containerAction.Environment[0].Key.ToString());
|
||||
Assert.Equal("foo", containerAction.Environment[0].Value.ToString());
|
||||
Assert.Equal("Url", containerAction.Environment[1].Key.ToString());
|
||||
Assert.Equal("bar", containerAction.Environment[1].Value.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -109,6 +155,52 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_ContainerAction_Dockerfile_Pre_DefaultCondition()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_init_default.yml"));
|
||||
|
||||
//Assert
|
||||
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.Container, result.Execution.ExecutionType);
|
||||
|
||||
var containerAction = result.Execution as ContainerActionExecutionData;
|
||||
|
||||
Assert.Equal("Dockerfile", containerAction.Image);
|
||||
Assert.Equal("main.sh", containerAction.EntryPoint);
|
||||
Assert.Equal("init.sh", containerAction.Init);
|
||||
Assert.Equal("always()", containerAction.InitCondition);
|
||||
Assert.Equal("bzz", containerAction.Arguments[0].ToString());
|
||||
Assert.Equal("Token", containerAction.Environment[0].Key.ToString());
|
||||
Assert.Equal("foo", containerAction.Environment[0].Value.ToString());
|
||||
Assert.Equal("Url", containerAction.Environment[1].Key.ToString());
|
||||
Assert.Equal("bar", containerAction.Environment[1].Value.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -321,6 +413,94 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_NodeAction_Pre()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction_init.yml"));
|
||||
|
||||
//Assert
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
Assert.Equal(1, result.Deprecated.Count);
|
||||
|
||||
Assert.True(result.Deprecated.ContainsKey("greeting"));
|
||||
result.Deprecated.TryGetValue("greeting", out string value);
|
||||
Assert.Equal("This property has been deprecated", value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType);
|
||||
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("init.js", nodeAction.Init);
|
||||
Assert.Equal("cancelled()", nodeAction.InitCondition);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_NodeAction_Init_DefaultCondition()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction_init_default.yml"));
|
||||
|
||||
//Assert
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
Assert.Equal(1, result.Deprecated.Count);
|
||||
|
||||
Assert.True(result.Deprecated.ContainsKey("greeting"));
|
||||
result.Deprecated.TryGetValue("greeting", out string value);
|
||||
Assert.Equal("This property has been deprecated", value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType);
|
||||
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("init.js", nodeAction.Init);
|
||||
Assert.Equal("always()", nodeAction.InitCondition);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
|
||||
@@ -199,20 +199,20 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
|
||||
var postRunner1 = hc.CreateService<IActionRunner>();
|
||||
postRunner1.Action = new Pipelines.ActionStep() { Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner1.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner1.Stage = ActionRunStage.Post;
|
||||
postRunner1.Condition = "always()";
|
||||
postRunner1.DisplayName = "post1";
|
||||
|
||||
|
||||
var postRunner2 = hc.CreateService<IActionRunner>();
|
||||
postRunner2.Action = new Pipelines.ActionStep() { Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner2.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner2.Stage = ActionRunStage.Post;
|
||||
postRunner2.Condition = "always()";
|
||||
postRunner2.DisplayName = "post2";
|
||||
|
||||
action1.RegisterPostJobStep("post1", postRunner1);
|
||||
action2.RegisterPostJobStep("post2", postRunner2);
|
||||
action1.RegisterPostJobStep(postRunner1);
|
||||
action2.RegisterPostJobStep(postRunner2);
|
||||
|
||||
Assert.NotNull(jobContext.JobSteps);
|
||||
Assert.NotNull(jobContext.PostJobSteps);
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>()))
|
||||
.Returns(Task.FromResult(new List<JobExtensionRunner>()));
|
||||
.Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>(), new Dictionary<Guid, IActionRunner>())));
|
||||
|
||||
List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>()))
|
||||
.Returns(Task.FromResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }));
|
||||
.Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }, new Dictionary<Guid, IActionRunner>())));
|
||||
|
||||
List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);
|
||||
|
||||
|
||||
27
src/Test/TestData/dockerfileaction_init.yml
Normal file
27
src/Test/TestData/dockerfileaction_init.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- 'bzz'
|
||||
entrypoint: 'main.sh'
|
||||
env:
|
||||
Token: foo
|
||||
Url: bar
|
||||
pre-entrypoint: 'init.sh'
|
||||
pre-if: 'success()'
|
||||
26
src/Test/TestData/dockerfileaction_init_default.yml
Normal file
26
src/Test/TestData/dockerfileaction_init_default.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- 'bzz'
|
||||
entrypoint: 'main.sh'
|
||||
env:
|
||||
Token: foo
|
||||
Url: bar
|
||||
pre-entrypoint: 'init.sh'
|
||||
22
src/Test/TestData/nodeaction_init.yml
Normal file
22
src/Test/TestData/nodeaction_init.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
deprecationMessage: 'This property has been deprecated'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'main.js'
|
||||
pre: 'init.js'
|
||||
pre-if: 'cancelled()'
|
||||
21
src/Test/TestData/nodeaction_init_default.yml
Normal file
21
src/Test/TestData/nodeaction_init_default.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
deprecationMessage: 'This property has been deprecated'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'main.js'
|
||||
pre: 'init.js'
|
||||
Reference in New Issue
Block a user