Compare commits

..

10 Commits

Author SHA1 Message Date
TingluoHuang
c44b34e436 c 2020-03-24 16:14:02 -04:00
TingluoHuang
738602ee55 c 2020-03-22 22:37:23 -04:00
TingluoHuang
502b9f06f2 c 2020-03-21 22:25:54 -04:00
TingluoHuang
310530add6 c 2020-03-20 22:54:03 -04:00
TingluoHuang
de07637563 c 2020-03-06 21:28:10 -05:00
Josh Gross
94e7560ccd Add event type to credential call (#352)
* Add event type to credential call

* Move events to contants
2020-03-02 11:22:45 -05:00
Lokesh Gopu
d80ab095a5 Update runnerversion (#348)
* Update runnerversion

* Update runnerversion
2020-02-28 13:28:33 -05:00
Josh Gross
2efd6f70e2 Use the Uri Scheme in the register runner url (#345) 2020-02-25 18:30:33 -05:00
Lokesh Gopu
a6f144b014 Update Runner Register GitHub API URL to Support Org-level Runner (#339)
* Update GitHub API URL

* pr comments

* Updated GitHub API URL
2020-02-24 09:15:15 -05:00
eric sciple
5294a3ee06 commands translate file path from container action (#331) 2020-02-12 21:07:43 -05:00
29 changed files with 812 additions and 213 deletions

View File

@@ -136,6 +136,12 @@ namespace GitHub.Runner.Common
} }
} }
public static class RunnerEvent
{
public static readonly string Register = "register";
public static readonly string Remove = "remove";
}
public static class Pipeline public static class Pipeline
{ {
public static class Path public static class Path

View File

@@ -46,6 +46,7 @@ namespace GitHub.Runner.Common
Add<T>(extensions, "GitHub.Runner.Worker.SetOutputCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.SetOutputCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.SaveStateCommandExtension, 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.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.AddMaskCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.AddMatcherCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.AddMatcherCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.RemoveMatcherCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.RemoveMatcherCommandExtension, Runner.Worker");

View File

@@ -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<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 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<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 public sealed class JobServer : RunnerService, IJobServer
@@ -113,5 +114,11 @@ namespace GitHub.Runner.Common
CheckConnection(); CheckConnection();
return _taskClient.GetTimelineAsync(scopeIdentifier, hubName, planId, timelineId, includeRecords: true, cancellationToken: cancellationToken); 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);
}
} }
} }

View File

@@ -1,19 +1,18 @@
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common;
using GitHub.Runner.Common.Util; using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using GitHub.Services.Common; using GitHub.Services.Common;
using GitHub.Services.OAuth; using GitHub.Services.OAuth;
using GitHub.Services.WebApi; using GitHub.Services.WebApi;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace GitHub.Runner.Listener.Configuration namespace GitHub.Runner.Listener.Configuration
{ {
@@ -109,7 +108,7 @@ namespace GitHub.Runner.Listener.Configuration
{ {
runnerSettings.GitHubUrl = inputUrl; runnerSettings.GitHubUrl = inputUrl;
var githubToken = command.GetRunnerRegisterToken(); var githubToken = command.GetRunnerRegisterToken();
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken); GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken, Constants.RunnerEvent.Register);
runnerSettings.ServerUrl = authResult.TenantUrl; runnerSettings.ServerUrl = authResult.TenantUrl;
creds = authResult.ToVssCredentials(); creds = authResult.ToVssCredentials();
Trace.Info("cred retrieved via GitHub auth"); Trace.Info("cred retrieved via GitHub auth");
@@ -376,7 +375,7 @@ namespace GitHub.Runner.Listener.Configuration
else else
{ {
var githubToken = command.GetRunnerDeletionToken(); var githubToken = command.GetRunnerDeletionToken();
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken); GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken, Constants.RunnerEvent.Remove);
creds = authResult.ToVssCredentials(); creds = authResult.ToVssCredentials();
Trace.Info("cred retrieved via GitHub auth"); Trace.Info("cred retrieved via GitHub auth");
} }
@@ -520,17 +519,23 @@ namespace GitHub.Runner.Listener.Configuration
} }
} }
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken) private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent)
{ {
var gitHubUrl = new UriBuilder(githubUrl); var gitHubUrlBuilder = new UriBuilder(githubUrl);
var githubApiUrl = $"https://api.{gitHubUrl.Host}/repos/{gitHubUrl.Path.Trim('/')}/actions-runners/registration"; var githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/actions/runner-registration";
using (var httpClientHandler = HostContext.CreateHttpClientHandler()) using (var httpClientHandler = HostContext.CreateHttpClientHandler())
using (var httpClient = new HttpClient(httpClientHandler)) using (var httpClient = new HttpClient(httpClientHandler))
{ {
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("RemoteAuth", githubToken); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("RemoteAuth", githubToken);
httpClient.DefaultRequestHeaders.UserAgent.Add(HostContext.UserAgent); httpClient.DefaultRequestHeaders.UserAgent.Add(HostContext.UserAgent);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.shuri-preview+json"));
var response = await httpClient.PostAsync(githubApiUrl, new StringContent("", null, "application/json")); var bodyObject = new Dictionary<string, string>()
{
{"url", githubUrl},
{"runner_event", runnerEvent}
};
var response = await httpClient.PostAsync(githubApiUrl, new StringContent(StringUtil.ConvertToJson(bodyObject), null, "application/json"));
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {

View File

@@ -1,6 +1,7 @@
using GitHub.DistributedTask.Pipelines; using GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common.Util; using GitHub.Runner.Common.Util;
using GitHub.Runner.Worker.Container;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -15,14 +16,14 @@ namespace GitHub.Runner.Worker
{ {
void EnablePluginInternalCommand(); void EnablePluginInternalCommand();
void DisablePluginInternalCommand(); void DisablePluginInternalCommand();
bool TryProcessCommand(IExecutionContext context, string input); bool TryProcessCommand(IExecutionContext context, string input, ContainerInfo container);
} }
public sealed class ActionCommandManager : RunnerService, IActionCommandManager public sealed class ActionCommandManager : RunnerService, IActionCommandManager
{ {
private const string _stopCommand = "stop-commands"; private const string _stopCommand = "stop-commands";
private readonly Dictionary<string, IActionCommandExtension> _commandExtensions = new Dictionary<string, IActionCommandExtension>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, IActionCommandExtension> _commandExtensions = new Dictionary<string, IActionCommandExtension>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> _registeredCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _registeredCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly object _commandSerializeLock = new object(); private readonly object _commandSerializeLock = new object();
private bool _stopProcessCommand = false; private bool _stopProcessCommand = false;
private string _stopToken = null; private string _stopToken = null;
@@ -58,7 +59,7 @@ namespace GitHub.Runner.Worker
_registeredCommands.Remove("internal-set-repo-path"); _registeredCommands.Remove("internal-set-repo-path");
} }
public bool TryProcessCommand(IExecutionContext context, string input) public bool TryProcessCommand(IExecutionContext context, string input, ContainerInfo container)
{ {
if (string.IsNullOrEmpty(input)) if (string.IsNullOrEmpty(input))
{ {
@@ -114,7 +115,7 @@ namespace GitHub.Runner.Worker
try try
{ {
extension.ProcessCommand(context, input, actionCommand); extension.ProcessCommand(context, input, actionCommand, container);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -140,7 +141,7 @@ namespace GitHub.Runner.Worker
string Command { get; } string Command { get; }
bool OmitEcho { get; } bool OmitEcho { get; }
void ProcessCommand(IExecutionContext context, string line, ActionCommand command); void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container);
} }
public sealed class InternalPluginSetRepoPathCommandExtension : RunnerService, IActionCommandExtension public sealed class InternalPluginSetRepoPathCommandExtension : RunnerService, IActionCommandExtension
@@ -150,7 +151,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
if (!command.Properties.TryGetValue(SetRepoPathCommandProperties.repoFullName, out string repoFullName) || string.IsNullOrEmpty(repoFullName)) if (!command.Properties.TryGetValue(SetRepoPathCommandProperties.repoFullName, out string repoFullName) || string.IsNullOrEmpty(repoFullName))
{ {
@@ -180,7 +181,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
if (!command.Properties.TryGetValue(SetEnvCommandProperties.Name, out string envName) || string.IsNullOrEmpty(envName)) if (!command.Properties.TryGetValue(SetEnvCommandProperties.Name, out string envName) || string.IsNullOrEmpty(envName))
{ {
@@ -205,7 +206,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName)) if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName))
{ {
@@ -229,7 +230,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName)) if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName))
{ {
@@ -253,7 +254,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
if (string.IsNullOrWhiteSpace(command.Data)) if (string.IsNullOrWhiteSpace(command.Data))
{ {
@@ -279,7 +280,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
ArgUtil.NotNullOrEmpty(command.Data, "path"); ArgUtil.NotNullOrEmpty(command.Data, "path");
context.PrependPath.RemoveAll(x => string.Equals(x, command.Data, StringComparison.CurrentCulture)); context.PrependPath.RemoveAll(x => string.Equals(x, command.Data, StringComparison.CurrentCulture));
@@ -287,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 sealed class AddMatcherCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "add-matcher"; public string Command => "add-matcher";
@@ -294,7 +319,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
var file = command.Data; var file = command.Data;
@@ -306,9 +331,9 @@ namespace GitHub.Runner.Worker
} }
// Translate file path back from container path // Translate file path back from container path
if (context.Container != null) if (container != null)
{ {
file = context.Container.TranslateToHostPath(file); file = container.TranslateToHostPath(file);
} }
// Root the path // Root the path
@@ -341,7 +366,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
command.Properties.TryGetValue(RemoveMatcherCommandProperties.Owner, out string owner); command.Properties.TryGetValue(RemoveMatcherCommandProperties.Owner, out string owner);
var file = command.Data; var file = command.Data;
@@ -369,9 +394,9 @@ namespace GitHub.Runner.Worker
else else
{ {
// Translate file path back from container path // Translate file path back from container path
if (context.Container != null) if (container != null)
{ {
file = context.Container.TranslateToHostPath(file); file = container.TranslateToHostPath(file);
} }
// Root the path // Root the path
@@ -409,7 +434,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command) public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command, ContainerInfo container)
{ {
context.Debug(command.Data); context.Debug(command.Data);
} }
@@ -437,7 +462,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command) public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command, ContainerInfo container)
{ {
command.Properties.TryGetValue(IssueCommandProperties.File, out string file); command.Properties.TryGetValue(IssueCommandProperties.File, out string file);
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line); command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
@@ -454,10 +479,10 @@ namespace GitHub.Runner.Worker
{ {
issue.Category = "Code"; issue.Category = "Code";
if (context.Container != null) if (container != null)
{ {
// Translate file path back from container path // Translate file path back from container path
file = context.Container.TranslateToHostPath(file); file = container.TranslateToHostPath(file);
command.Properties[IssueCommandProperties.File] = file; command.Properties[IssueCommandProperties.File] = file;
} }
@@ -517,7 +542,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
var data = this is GroupCommandExtension ? command.Data : string.Empty; var data = this is GroupCommandExtension ? command.Data : string.Empty;
context.Output($"##[{Command}]{data}"); context.Output($"##[{Command}]{data}");
@@ -531,7 +556,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
ArgUtil.NotNullOrEmpty(command.Data, "value"); ArgUtil.NotNullOrEmpty(command.Data, "value");

View File

@@ -21,11 +21,24 @@ using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplat
namespace GitHub.Runner.Worker 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))] [ServiceLocator(Default = typeof(ActionManager))]
public interface IActionManager : IRunnerService public interface IActionManager : IRunnerService
{ {
Dictionary<Guid, ContainerInfo> CachedActionContainers { get; } 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); 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>(); private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>();
public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers; 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(executionContext, nameof(executionContext));
ArgUtil.NotNull(steps, nameof(steps)); 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, List<Guid>> imagesToBuild = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, ActionContainer> imagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase); Dictionary<string, ActionContainer> imagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase);
List<JobExtensionRunner> containerSetupSteps = new List<JobExtensionRunner>(); List<JobExtensionRunner> containerSetupSteps = new List<JobExtensionRunner>();
Dictionary<Guid, IActionRunner> preStepTracker = new Dictionary<Guid, IActionRunner>();
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>(); IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
// TODO: Depreciate the PREVIEW_ACTION_TOKEN // TODO: Depreciate the PREVIEW_ACTION_TOKEN
@@ -111,6 +125,21 @@ namespace GitHub.Runner.Worker
imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo; 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 #endif
return containerSetupSteps; return new PrepareResult(containerSetupSteps, preStepTracker);
} }
public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action) public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action)
@@ -162,6 +191,7 @@ namespace GitHub.Runner.Worker
Data = new ActionDefinitionData() Data = new ActionDefinitionData()
}; };
// bool localAction = false;
if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry) if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry)
{ {
Trace.Info("Load action that reference container from registry."); Trace.Info("Load action that reference container from registry.");
@@ -180,6 +210,7 @@ namespace GitHub.Runner.Worker
var repoAction = action.Reference as Pipelines.RepositoryPathReference; var repoAction = action.Reference as Pipelines.RepositoryPathReference;
if (string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase)) if (string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
{ {
// localAction = true;
actionDirectory = executionContext.GetGitHubContext("workspace"); actionDirectory = executionContext.GetGitHubContext("workspace");
if (!string.IsNullOrEmpty(repoAction.Path)) if (!string.IsNullOrEmpty(repoAction.Path))
{ {
@@ -239,6 +270,11 @@ namespace GitHub.Runner.Worker
Trace.Info($"Action container env: {StringUtil.ConvertToJson(containerAction.Environment)}."); 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)) if (!string.IsNullOrEmpty(containerAction.EntryPoint))
{ {
Trace.Info($"Action container entrypoint: {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) else if (definition.Data.Execution.ExecutionType == ActionExecutionType.NodeJS)
{ {
var nodeAction = definition.Data.Execution as NodeJSActionExecutionData; 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 node.js file: {nodeAction.Script}.");
Trace.Info($"Action cleanup node.js file: {nodeAction.Cleanup ?? "N/A"}."); 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 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 override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
public string Image { get; set; } public string Image { get; set; }
@@ -782,6 +821,8 @@ namespace GitHub.Runner.Worker
public MappingToken Environment { get; set; } public MappingToken Environment { get; set; }
public string Init { get; set; }
public string Cleanup { get; set; } public string Cleanup { get; set; }
} }
@@ -789,10 +830,14 @@ namespace GitHub.Runner.Worker
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.NodeJS; 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 override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
public string Script { get; set; } public string Script { get; set; }
public string Init { get; set; }
public string Cleanup { get; set; } public string Cleanup { get; set; }
} }
@@ -800,6 +845,9 @@ namespace GitHub.Runner.Worker
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.Plugin; public override ActionExecutionType ExecutionType => ActionExecutionType.Plugin;
public override bool HasInit => false;
public override bool HasMain => true;
public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup); public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup);
public string Plugin { get; set; } public string Plugin { get; set; }
@@ -810,16 +858,20 @@ namespace GitHub.Runner.Worker
public sealed class ScriptActionExecutionData : ActionExecutionData public sealed class ScriptActionExecutionData : ActionExecutionData
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.Script; public override ActionExecutionType ExecutionType => ActionExecutionType.Script;
public override bool HasInit => false;
public override bool HasMain => true;
public override bool HasCleanup => false; public override bool HasCleanup => false;
} }
public abstract class ActionExecutionData public abstract class ActionExecutionData
{ {
private string _initCondition = $"{Constants.Expressions.Always}()";
private string _cleanupCondition = $"{Constants.Expressions.Always}()"; private string _cleanupCondition = $"{Constants.Expressions.Always}()";
public abstract ActionExecutionType ExecutionType { get; } public abstract ActionExecutionType ExecutionType { get; }
public abstract bool HasInit { get; }
public abstract bool HasMain { get; }
public abstract bool HasCleanup { get; } public abstract bool HasCleanup { get; }
public string CleanupCondition public string CleanupCondition
@@ -827,6 +879,12 @@ namespace GitHub.Runner.Worker
get { return _cleanupCondition; } get { return _cleanupCondition; }
set { _cleanupCondition = value; } set { _cleanupCondition = value; }
} }
public string InitCondition
{
get { return _initCondition; }
set { _initCondition = value; }
}
} }
public class ContainerSetupInfo public class ContainerSetupInfo

View File

@@ -280,6 +280,9 @@ namespace GitHub.Runner.Worker
var envToken = default(MappingToken); var envToken = default(MappingToken);
var mainToken = default(StringToken); var mainToken = default(StringToken);
var pluginToken = default(StringToken); var pluginToken = default(StringToken);
var preToken = default(StringToken);
var preEntrypointToken = default(StringToken);
var preIfToken = default(StringToken);
var postToken = default(StringToken); var postToken = default(StringToken);
var postEntrypointToken = default(StringToken); var postEntrypointToken = default(StringToken);
var postIfToken = default(StringToken); var postIfToken = default(StringToken);
@@ -318,6 +321,15 @@ namespace GitHub.Runner.Worker
case "post-if": case "post-if":
postIfToken = run.Value.AssertString("post-if"); postIfToken = run.Value.AssertString("post-if");
break; 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: default:
Trace.Info($"Ignore run property {runsKey}."); Trace.Info($"Ignore run property {runsKey}.");
break; break;
@@ -340,6 +352,8 @@ namespace GitHub.Runner.Worker
Arguments = argsToken, Arguments = argsToken,
EntryPoint = entrypointToken?.Value, EntryPoint = entrypointToken?.Value,
Environment = envToken, Environment = envToken,
Init = preEntrypointToken?.Value,
InitCondition = preIfToken?.Value ?? "always()",
Cleanup = postEntrypointToken?.Value, Cleanup = postEntrypointToken?.Value,
CleanupCondition = postIfToken?.Value ?? "always()" CleanupCondition = postIfToken?.Value ?? "always()"
}; };
@@ -356,6 +370,8 @@ namespace GitHub.Runner.Worker
return new NodeJSActionExecutionData() return new NodeJSActionExecutionData()
{ {
Script = mainToken.Value, Script = mainToken.Value,
Init = preToken?.Value,
InitCondition = preIfToken?.Value ?? "always()",
Cleanup = postToken?.Value, Cleanup = postToken?.Value,
CleanupCondition = postIfToken?.Value ?? "always()" CleanupCondition = postIfToken?.Value ?? "always()"
}; };

View File

@@ -18,6 +18,7 @@ namespace GitHub.Runner.Worker
{ {
public enum ActionRunStage public enum ActionRunStage
{ {
Pre,
Main, Main,
Post, Post,
} }
@@ -81,20 +82,18 @@ namespace GitHub.Runner.Worker
ActionExecutionData handlerData = definition.Data?.Execution; ActionExecutionData handlerData = definition.Data?.Execution;
ArgUtil.NotNull(handlerData, nameof(handlerData)); 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. // 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 // 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; string postDisplayName = $"Post {this.DisplayName}";
if (this.DisplayName.StartsWith(PipelineTemplateConstants.RunDisplayPrefix))
{
postDisplayName = $"Post {this.DisplayName.Substring(PipelineTemplateConstants.RunDisplayPrefix.Length)}";
}
else
{
postDisplayName = $"Post {this.DisplayName}";
}
var repositoryReference = Action.Reference as RepositoryPathReference; var repositoryReference = Action.Reference as RepositoryPathReference;
var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}"; var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" : var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
@@ -108,7 +107,7 @@ namespace GitHub.Runner.Worker
actionRunner.Condition = handlerData.CleanupCondition; actionRunner.Condition = handlerData.CleanupCondition;
actionRunner.DisplayName = postDisplayName; actionRunner.DisplayName = postDisplayName;
ExecutionContext.RegisterPostJobStep($"{actionRunner.Action.Name}_post", actionRunner); ExecutionContext.RegisterPostJobStep(actionRunner);
} }
IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>(); IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>();

View File

@@ -49,7 +49,7 @@ namespace GitHub.Runner.Worker
data: data); data: data);
executionContext.Debug($"Register post job cleanup for stopping/deleting containers."); 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. // Check whether we are inside a container.
// Our container feature requires to map working directory from host to the container. // Our container feature requires to map working directory from host to the container.

View File

@@ -21,6 +21,7 @@ using System.Collections;
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating; using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
using Pipelines = GitHub.DistributedTask.Pipelines; using Pipelines = GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.Expressions2; using GitHub.DistributedTask.Expressions2;
using System.Diagnostics;
namespace GitHub.Runner.Worker namespace GitHub.Runner.Worker
{ {
@@ -98,7 +99,9 @@ namespace GitHub.Runner.Worker
// others // others
void ForceTaskComplete(); void ForceTaskComplete();
void RegisterPostJobStep(string refName, IStep step); void RegisterPostJobStep(IStep step);
Task<string> GetGitHubToken();
Task UpdateGitHubTokenInContext();
} }
public sealed class ExecutionContext : RunnerService, IExecutionContext public sealed class ExecutionContext : RunnerService, IExecutionContext
@@ -110,6 +113,7 @@ namespace GitHub.Runner.Worker
private readonly object _loggerLock = new object(); private readonly object _loggerLock = new object();
private readonly HashSet<string> _outputvariables = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _outputvariables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly object _matchersLock = new object(); private readonly object _matchersLock = new object();
public readonly List<Task> _getGitHubTokenTasks = new List<Task>();
private event OnMatcherChanged _onMatcherChanged; private event OnMatcherChanged _onMatcherChanged;
@@ -130,6 +134,13 @@ namespace GitHub.Runner.Worker
// only job level ExecutionContext will track throttling delay. // only job level ExecutionContext will track throttling delay.
private long _totalThrottlingDelayInMilliseconds = 0; 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 Guid Id => _record.Id;
public string ScopeName { get; private set; } public string ScopeName { get; private set; }
public string ContextName { get; private set; } public string ContextName { get; private set; }
@@ -154,6 +165,9 @@ namespace GitHub.Runner.Worker
// Only job level ExecutionContext has PostJobSteps // Only job level ExecutionContext has PostJobSteps
public Stack<IStep> PostJobSteps { get; private set; } 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; } 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); Root.PostJobSteps.Push(step);
} }
@@ -608,6 +681,7 @@ namespace GitHub.Runner.Worker
ExpressionValues["env"] = new CaseSensitiveDictionaryContextData(); ExpressionValues["env"] = new CaseSensitiveDictionaryContextData();
#endif #endif
_githubTokenExpireTimer.Start();
// Prepend Path // Prepend Path
PrependPath = new List<string>(); PrependPath = new List<string>();
@@ -618,6 +692,14 @@ namespace GitHub.Runner.Worker
// PostJobSteps for job ExecutionContext // PostJobSteps for job ExecutionContext
PostJobSteps = new Stack<IStep>(); 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. // Job timeline record.
InitializeTimelineRecord( InitializeTimelineRecord(
timelineId: message.Timeline.Id, 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) if (!_expandedForPostJob)
{ {
@@ -827,7 +909,8 @@ namespace GitHub.Runner.Worker
_childTimelineRecordOrder = _childTimelineRecordOrder * 2; _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);
} }
} }

View File

@@ -82,6 +82,10 @@ namespace GitHub.Runner.Worker.Handlers
container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint"); container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint");
} }
} }
else if (stage == ActionRunStage.Pre)
{
container.ContainerEntryPoint = Data.Init;
}
else if (stage == ActionRunStage.Post) else if (stage == ActionRunStage.Post)
{ {
container.ContainerEntryPoint = Data.Cleanup; container.ContainerEntryPoint = Data.Cleanup;

View File

@@ -60,6 +60,10 @@ namespace GitHub.Runner.Worker.Handlers
{ {
target = Data.Script; target = Data.Script;
} }
else if (stage == ActionRunStage.Pre)
{
target = Data.Init;
}
else if (stage == ActionRunStage.Post) else if (stage == ActionRunStage.Post)
{ {
target = Data.Cleanup; target = Data.Cleanup;

View File

@@ -84,7 +84,7 @@ namespace GitHub.Runner.Worker.Handlers
{ {
// This does not need to be inside of a critical section. // This does not need to be inside of a critical section.
// The logging queues and command handlers are thread-safe. // The logging queues and command handlers are thread-safe.
if (_commandManager.TryProcessCommand(_executionContext, line)) if (_commandManager.TryProcessCommand(_executionContext, line, _container))
{ {
return; return;
} }

View File

@@ -116,8 +116,8 @@ namespace GitHub.Runner.Worker
// Download actions not already in the cache // Download actions not already in the cache
Trace.Info("Downloading actions"); Trace.Info("Downloading actions");
var actionManager = HostContext.GetService<IActionManager>(); var actionManager = HostContext.GetService<IActionManager>();
var prepareSteps = await actionManager.PrepareActionsAsync(context, message.Steps); var prepareResult = await actionManager.PrepareActionsAsync(context, message.Steps);
preJobSteps.AddRange(prepareSteps); preJobSteps.AddRange(prepareResult.ContainerSetupSteps);
// Add start-container steps, record and stop-container steps // Add start-container steps, record and stop-container steps
if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0) if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0)
@@ -158,9 +158,23 @@ namespace GitHub.Runner.Worker
actionRunner.TryEvaluateDisplayName(contextData, context); actionRunner.TryEvaluateDisplayName(contextData, context);
jobSteps.Add(actionRunner); 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 // Create execution context for pre-job steps
foreach (var step in preJobSteps) foreach (var step in preJobSteps)
{ {
@@ -171,6 +185,12 @@ namespace GitHub.Runner.Worker
Guid stepId = Guid.NewGuid(); Guid stepId = Guid.NewGuid();
extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N")); 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 // Create execution context for job steps
@@ -179,7 +199,14 @@ namespace GitHub.Runner.Worker
if (step is IActionRunner actionStep) if (step is IActionRunner actionStep)
{ {
ArgUtil.NotNull(actionStep, step.DisplayName); 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);
}
} }
} }

View File

@@ -75,6 +75,7 @@ namespace GitHub.Runner.Worker
// Start // Start
step.ExecutionContext.Start(); step.ExecutionContext.Start();
await step.ExecutionContext.UpdateGitHubTokenInContext();
// Initialize scope // Initialize scope
if (InitializeScope(step, scopeInputs)) if (InitializeScope(step, scopeInputs))

View File

@@ -43,6 +43,8 @@
"entrypoint": "non-empty-string", "entrypoint": "non-empty-string",
"args": "container-runs-args", "args": "container-runs-args",
"env": "container-runs-env", "env": "container-runs-env",
"pre-entrypoint": "non-empty-string",
"pre-if": "non-empty-string",
"post-entrypoint": "non-empty-string", "post-entrypoint": "non-empty-string",
"post-if": "non-empty-string" "post-if": "non-empty-string"
} }
@@ -67,6 +69,8 @@
"properties": { "properties": {
"using": "non-empty-string", "using": "non-empty-string",
"main": "non-empty-string", "main": "non-empty-string",
"pre": "non-empty-string",
"pre-if": "non-empty-string",
"post": "non-empty-string", "post": "non-empty-string",
"post-if": "non-empty-string" "post-if": "non-empty-string"
} }

View File

@@ -317,5 +317,40 @@ namespace GitHub.DistributedTask.WebApi
userState: userState, userState: userState,
cancellationToken: cancellationToken); 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);
}
} }
} }

View File

@@ -4,6 +4,17 @@ using System.Runtime.Serialization;
namespace GitHub.DistributedTask.WebApi 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] [DataContract]
public class Issue public class Issue
{ {

View File

@@ -1,8 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Worker; using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container;
using Moq; using Moq;
using Xunit; using Xunit;
using Pipelines = GitHub.DistributedTask.Pipelines; using Pipelines = GitHub.DistributedTask.Pipelines;
@@ -11,47 +13,35 @@ namespace GitHub.Runner.Common.Tests.Worker
{ {
public sealed class ActionCommandManagerL0 public sealed class ActionCommandManagerL0
{ {
private ActionCommandManager _commandManager;
private Mock<IExecutionContext> _ec;
private Mock<IExtensionManager> _extensionManager;
private Mock<IPipelineDirectoryManager> _pipelineDirectoryManager;
[Fact] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
public void EnablePluginInternalCommand() public void EnablePluginInternalCommand()
{ {
using (TestHostContext _hc = new TestHostContext(this)) using (TestHostContext hc = CreateTestContext())
{ {
var extensionManger = new Mock<IExtensionManager>();
var directoryManager = new Mock<IPipelineDirectoryManager>();
var pluginCommand = new InternalPluginSetRepoPathCommandExtension();
pluginCommand.Initialize(_hc);
var envCommand = new SetEnvCommandExtension();
envCommand.Initialize(_hc);
extensionManger.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { pluginCommand, envCommand });
_hc.SetSingleton<IExtensionManager>(extensionManger.Object);
_hc.SetSingleton<IPipelineDirectoryManager>(directoryManager.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) => .Returns((string tag, string line) =>
{ {
_hc.GetTrace().Info($"{tag} {line}"); hc.GetTrace().Info($"{tag} {line}");
return 1; return 1;
}); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) => .Callback((Issue issue, string message) =>
{ {
_hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}"); hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
}); });
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
commandManager.EnablePluginInternalCommand(); _commandManager.EnablePluginInternalCommand();
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath", null));
directoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once); _pipelineDirectoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
} }
} }
@@ -60,47 +50,29 @@ namespace GitHub.Runner.Common.Tests.Worker
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
public void DisablePluginInternalCommand() public void DisablePluginInternalCommand()
{ {
using (TestHostContext _hc = new TestHostContext(this)) using (TestHostContext hc = CreateTestContext())
{ {
var extensionManger = new Mock<IExtensionManager>();
var directoryManager = new Mock<IPipelineDirectoryManager>();
var pluginCommand = new InternalPluginSetRepoPathCommandExtension();
pluginCommand.Initialize(_hc);
var envCommand = new SetEnvCommandExtension();
envCommand.Initialize(_hc);
extensionManger.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { pluginCommand, envCommand });
_hc.SetSingleton<IExtensionManager>(extensionManger.Object);
_hc.SetSingleton<IPipelineDirectoryManager>(directoryManager.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) => .Returns((string tag, string line) =>
{ {
_hc.GetTrace().Info($"{tag} {line}"); hc.GetTrace().Info($"{tag} {line}");
return 1; return 1;
}); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) => .Callback((Issue issue, string message) =>
{ {
_hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}"); hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
}); });
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
commandManager.EnablePluginInternalCommand(); _commandManager.EnablePluginInternalCommand();
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath", null));
commandManager.DisablePluginInternalCommand(); _commandManager.DisablePluginInternalCommand();
Assert.False(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath")); Assert.False(_commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath", null));
directoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once); _pipelineDirectoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
} }
} }
@@ -109,42 +81,27 @@ namespace GitHub.Runner.Common.Tests.Worker
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
public void StopProcessCommand() public void StopProcessCommand()
{ {
using (TestHostContext _hc = new TestHostContext(this)) using (TestHostContext hc = CreateTestContext())
{ {
var extensionManger = new Mock<IExtensionManager>();
var pluginCommand = new InternalPluginSetRepoPathCommandExtension();
pluginCommand.Initialize(_hc);
var envCommand = new SetEnvCommandExtension();
envCommand.Initialize(_hc);
extensionManger.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { pluginCommand, envCommand });
_hc.SetSingleton<IExtensionManager>(extensionManger.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) => .Returns((string tag, string line) =>
{ {
_hc.GetTrace().Info($"{tag} {line}"); hc.GetTrace().Info($"{tag} {line}");
return 1; return 1;
}); });
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())) _ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
.Callback((Issue issue, string message) => .Callback((Issue issue, string message) =>
{ {
_hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}"); hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
}); });
_ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>()); _ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>());
ActionCommandManager commandManager = new ActionCommandManager(); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[stop-commands]stopToken", null));
commandManager.Initialize(_hc); Assert.False(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[stopToken]", null));
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[stop-commands]stopToken")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
Assert.False(commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar"));
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[stopToken]"));
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar"));
} }
} }
@@ -153,41 +110,29 @@ namespace GitHub.Runner.Common.Tests.Worker
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
public void EchoProcessCommand() public void EchoProcessCommand()
{ {
using (TestHostContext _hc = new TestHostContext(this)) using (TestHostContext hc = CreateTestContext())
{ {
var extensionManager = new Mock<IExtensionManager>();
var echoCommand = new EchoCommandExtension();
echoCommand.Initialize(_hc);
extensionManager.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { echoCommand });
_hc.SetSingleton<IExtensionManager>(extensionManager.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) => .Returns((string tag, string line) =>
{ {
_hc.GetTrace().Info($"{tag} {line}"); hc.GetTrace().Info($"{tag} {line}");
return 1; return 1;
}); });
_ec.SetupAllProperties(); _ec.SetupAllProperties();
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
Assert.False(_ec.Object.EchoOnActionCommand); Assert.False(_ec.Object.EchoOnActionCommand);
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::on")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::on", null));
Assert.True(_ec.Object.EchoOnActionCommand); Assert.True(_ec.Object.EchoOnActionCommand);
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::off")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::off", null));
Assert.False(_ec.Object.EchoOnActionCommand); Assert.False(_ec.Object.EchoOnActionCommand);
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::ON")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::ON", null));
Assert.True(_ec.Object.EchoOnActionCommand); Assert.True(_ec.Object.EchoOnActionCommand);
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::Off ")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::Off ", null));
Assert.False(_ec.Object.EchoOnActionCommand); Assert.False(_ec.Object.EchoOnActionCommand);
} }
} }
@@ -197,7 +142,7 @@ namespace GitHub.Runner.Common.Tests.Worker
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
public void EchoProcessCommandDebugOn() public void EchoProcessCommandDebugOn()
{ {
using (TestHostContext _hc = new TestHostContext(this)) using (TestHostContext hc = CreateTestContext())
{ {
// Set up a few things // Set up a few things
// 1. Job request message (with ACTIONS_STEP_DEBUG = true) // 1. Job request message (with ACTIONS_STEP_DEBUG = true)
@@ -219,84 +164,135 @@ namespace GitHub.Runner.Common.Tests.Worker
var jobServerQueue = new Mock<IJobServerQueue>(); var jobServerQueue = new Mock<IJobServerQueue>();
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>())); jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
_hc.SetSingleton(jobServerQueue.Object); hc.SetSingleton(jobServerQueue.Object);
var extensionManager = new Mock<IExtensionManager>();
var echoCommand = new EchoCommandExtension();
echoCommand.Initialize(_hc);
extensionManager.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { echoCommand });
_hc.SetSingleton<IExtensionManager>(extensionManager.Object);
var configurationStore = new Mock<IConfigurationStore>(); var configurationStore = new Mock<IConfigurationStore>();
configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings()); configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings());
_hc.SetSingleton(configurationStore.Object); hc.SetSingleton(configurationStore.Object);
var pagingLogger = new Mock<IPagingLogger>(); var pagingLogger = new Mock<IPagingLogger>();
_hc.EnqueueInstance(pagingLogger.Object); hc.EnqueueInstance(pagingLogger.Object);
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
var _ec = new Runner.Worker.ExecutionContext();
_ec.Initialize(_hc);
// Initialize the job (to exercise logic that sets EchoOnActionCommand) // Initialize the job (to exercise logic that sets EchoOnActionCommand)
_ec.InitializeJob(jobRequest, System.Threading.CancellationToken.None); var ec = new Runner.Worker.ExecutionContext();
ec.Initialize(hc);
ec.InitializeJob(jobRequest, System.Threading.CancellationToken.None);
_ec.Complete(); ec.Complete();
Assert.True(_ec.EchoOnActionCommand); Assert.True(ec.EchoOnActionCommand);
Assert.True(commandManager.TryProcessCommand(_ec, "::echo::off")); Assert.True(_commandManager.TryProcessCommand(ec, "::echo::off", null));
Assert.False(_ec.EchoOnActionCommand); Assert.False(ec.EchoOnActionCommand);
Assert.True(commandManager.TryProcessCommand(_ec, "::echo::on")); Assert.True(_commandManager.TryProcessCommand(ec, "::echo::on", null));
Assert.True(_ec.EchoOnActionCommand); Assert.True(ec.EchoOnActionCommand);
} }
} }
[Fact] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
public void EchoProcessCommandInvalid() public void EchoProcessCommandInvalid()
{ {
using (TestHostContext _hc = new TestHostContext(this)) using (TestHostContext hc = CreateTestContext())
{ {
var extensionManager = new Mock<IExtensionManager>();
var echoCommand = new EchoCommandExtension();
echoCommand.Initialize(_hc);
extensionManager.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>() { echoCommand });
_hc.SetSingleton<IExtensionManager>(extensionManager.Object);
Mock<IExecutionContext> _ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())) _ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
.Returns((string tag, string line) => .Returns((string tag, string line) =>
{ {
_hc.GetTrace().Info($"{tag} {line}"); hc.GetTrace().Info($"{tag} {line}");
return 1; return 1;
}); });
_ec.SetupAllProperties(); _ec.SetupAllProperties();
ActionCommandManager commandManager = new ActionCommandManager();
commandManager.Initialize(_hc);
// Echo commands below are considered "processed", but are invalid // Echo commands below are considered "processed", but are invalid
// 1. Invalid echo value // 1. Invalid echo value
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::invalid")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::invalid", null));
Assert.Equal(TaskResult.Failed, _ec.Object.CommandResult); Assert.Equal(TaskResult.Failed, _ec.Object.CommandResult);
Assert.False(_ec.Object.EchoOnActionCommand); Assert.False(_ec.Object.EchoOnActionCommand);
// 2. No value // 2. No value
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::")); Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::", null));
Assert.Equal(TaskResult.Failed, _ec.Object.CommandResult); Assert.Equal(TaskResult.Failed, _ec.Object.CommandResult);
Assert.False(_ec.Object.EchoOnActionCommand); Assert.False(_ec.Object.EchoOnActionCommand);
} }
} }
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void AddMatcherTranslatesFilePath()
{
using (TestHostContext hc = CreateTestContext())
{
// Create a problem matcher config file
var hostDirectory = hc.GetDirectory(WellKnownDirectory.Temp);
var hostFile = Path.Combine(hostDirectory, "my-matcher.json");
Directory.CreateDirectory(hostDirectory);
var content = @"
{
""problemMatcher"": [
{
""owner"": ""my-matcher"",
""pattern"": [
{
""regexp"": ""^ERROR: (.+)$"",
""message"": 1
}
]
}
]
}";
File.WriteAllText(hostFile, content);
// Setup translation info
var container = new ContainerInfo();
var containerDirectory = "/some-container-directory";
var containerFile = Path.Combine(containerDirectory, "my-matcher.json");
container.AddPathTranslateMapping(hostDirectory, containerDirectory);
// Act
_commandManager.TryProcessCommand(_ec.Object, $"::add-matcher::{containerFile}", container);
// Assert
_ec.Verify(x => x.AddMatchers(It.IsAny<IssueMatchersConfig>()), Times.Once);
}
}
private TestHostContext CreateTestContext([CallerMemberName] string testName = "")
{
var hostContext = new TestHostContext(this, testName);
// Mock extension manager
_extensionManager = new Mock<IExtensionManager>();
var commands = new IActionCommandExtension[]
{
new AddMatcherCommandExtension(),
new EchoCommandExtension(),
new InternalPluginSetRepoPathCommandExtension(),
new SetEnvCommandExtension(),
};
foreach (var command in commands)
{
command.Initialize(hostContext);
}
_extensionManager.Setup(x => x.GetExtensions<IActionCommandExtension>())
.Returns(new List<IActionCommandExtension>(commands));
hostContext.SetSingleton<IExtensionManager>(_extensionManager.Object);
// Mock pipeline directory manager
_pipelineDirectoryManager = new Mock<IPipelineDirectoryManager>();
hostContext.SetSingleton<IPipelineDirectoryManager>(_pipelineDirectoryManager.Object);
// Execution context
_ec = new Mock<IExecutionContext>();
// Command manager
_commandManager = new ActionCommandManager();
_commandManager.Initialize(hostContext);
return hostContext;
}
} }
} }

View File

@@ -54,7 +54,7 @@ namespace GitHub.Runner.Common.Tests.Worker
}; };
//Act //Act
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
//Assert //Assert
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
@@ -164,7 +164,7 @@ namespace GitHub.Runner.Common.Tests.Worker
}; };
//Act //Act
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
Assert.True(steps.Count == 0); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfile");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
Assert.Equal(Path.Combine(actionDir, "Dockerfile"), (steps[0].Data as ContainerSetupInfo).Container.Dockerfile); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerfileRelativePath");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerHubImage");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionYamlFile_DockerHubImage");
//Act //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((steps[0].Data as ContainerSetupInfo).StepIds[0], actionId);
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithactionfileanddockerfile");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
@@ -557,7 +557,7 @@ namespace GitHub.Runner.Common.Tests.Worker
}; };
//Act //Act
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
//Assert //Assert
Assert.Equal(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
@@ -618,7 +618,7 @@ namespace GitHub.Runner.Common.Tests.Worker
}; };
//Act //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. // node.js based action doesn't need any extra steps to build/pull containers.
Assert.True(steps.Count == 0); 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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]

View File

@@ -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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]

View File

@@ -199,20 +199,20 @@ namespace GitHub.Runner.Common.Tests.Worker
var postRunner1 = hc.CreateService<IActionRunner>(); 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.Stage = ActionRunStage.Post;
postRunner1.Condition = "always()"; postRunner1.Condition = "always()";
postRunner1.DisplayName = "post1"; postRunner1.DisplayName = "post1";
var postRunner2 = hc.CreateService<IActionRunner>(); 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.Stage = ActionRunStage.Post;
postRunner2.Condition = "always()"; postRunner2.Condition = "always()";
postRunner2.DisplayName = "post2"; postRunner2.DisplayName = "post2";
action1.RegisterPostJobStep("post1", postRunner1); action1.RegisterPostJobStep(postRunner1);
action2.RegisterPostJobStep("post2", postRunner2); action2.RegisterPostJobStep(postRunner2);
Assert.NotNull(jobContext.JobSteps); Assert.NotNull(jobContext.JobSteps);
Assert.NotNull(jobContext.PostJobSteps); Assert.NotNull(jobContext.PostJobSteps);

View File

@@ -144,7 +144,7 @@ namespace GitHub.Runner.Common.Tests.Worker
jobExtension.Initialize(hc); jobExtension.Initialize(hc);
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>())) _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); List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);
@@ -179,7 +179,7 @@ namespace GitHub.Runner.Common.Tests.Worker
jobExtension.Initialize(hc); jobExtension.Initialize(hc);
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>())) _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); List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);

View File

@@ -973,8 +973,8 @@ namespace GitHub.Runner.Common.Tests.Worker
}); });
_commandManager = new Mock<IActionCommandManager>(); _commandManager = new Mock<IActionCommandManager>();
_commandManager.Setup(x => x.TryProcessCommand(It.IsAny<IExecutionContext>(), It.IsAny<string>())) _commandManager.Setup(x => x.TryProcessCommand(It.IsAny<IExecutionContext>(), It.IsAny<string>(), It.IsAny<ContainerInfo>()))
.Returns((IExecutionContext executionContext, string line) => .Returns((IExecutionContext executionContext, string line, ContainerInfo container) =>
{ {
if (line.IndexOf("##[some-command]") >= 0) if (line.IndexOf("##[some-command]") >= 0)
{ {

View 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()'

View 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'

View 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()'

View 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'

View File

@@ -1 +1 @@
2.165.0 2.165.2