mirror of
https://github.com/actions/runner.git
synced 2026-07-06 12:50:52 +08:00
Compare commits
2 Commits
users/tihu
...
users/tihu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e80ab803d6 | ||
|
|
4f40817c82 |
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -43,6 +43,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
# Set Path workaround for https://github.com/actions/virtual-environments/issues/263
|
||||
- run: |
|
||||
echo "::add-path::C:\Program Files\Git\mingw64\bin"
|
||||
echo "::add-path::C:\Program Files\Git\usr\bin"
|
||||
echo "::add-path::C:\Program Files\Git\bin"
|
||||
if: matrix.os == 'windows-latest'
|
||||
name: "Temp step to Set Path for Windows"
|
||||
|
||||
# Build runner layout
|
||||
- name: Build & Layout Release
|
||||
run: |
|
||||
|
||||
@@ -15,7 +15,6 @@ User wants to reference workflow variables defined in workflow yaml file for act
|
||||
Runner will create and populate the `env` context for every job execution using following logic:
|
||||
1. On job start, create `env` context with any environment variables in the job message, these are env defined in customer's YAML file's job/workflow level `env` section.
|
||||
2. Update `env` context when customer use `::set-env::` to set env at the runner level.
|
||||
3. Update `env` context with step's `env` block before each step runs.
|
||||
|
||||
The `env` context is only available in the runner, customer can't use the `env` context in any server evaluation part, just like the `runner` context
|
||||
|
||||
@@ -23,25 +22,17 @@ Example yaml:
|
||||
```yaml
|
||||
|
||||
env:
|
||||
env1: 10
|
||||
env2: 20
|
||||
env3: 30
|
||||
env1: 100
|
||||
jobs:
|
||||
build:
|
||||
env:
|
||||
env1: 100
|
||||
env2: 200
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo ${{ env.env1 }} // 1000
|
||||
echo $env1 // 1000
|
||||
echo $env2 // 200
|
||||
echo $env3 // 30
|
||||
if: env.env2 == 200 // true
|
||||
name: ${{ env.env1 }}_${{ env.env2 }} //1000_200
|
||||
env:
|
||||
env1: 1000
|
||||
echo ${{ env.env1 }}
|
||||
if: env.env2 == 200
|
||||
name: ${{ env.env1 }}_${{ env.env2 }}
|
||||
```
|
||||
|
||||
### Don't populate the `env` context with environment variables from runner machine.
|
||||
@@ -57,4 +48,4 @@ build:
|
||||
- uses: docker://ubuntu:18.04
|
||||
with:
|
||||
args: echo ${{env.USER}} <- what should customer expect this output? runner/root
|
||||
```
|
||||
```
|
||||
@@ -1,48 +0,0 @@
|
||||
# ADR 0297: Base64 Masking Trailing Characters
|
||||
|
||||
**Date** 2020-01-21
|
||||
|
||||
**Status** Proposed
|
||||
|
||||
## Context
|
||||
|
||||
The Runner registers a number of Value Encoders, which mask various encodings of a provided secret. Currently, we register a 3 base64 Encoders:
|
||||
- The base64 encoded secret
|
||||
- The secret with the first character removed then base64 encoded
|
||||
- The secret with the first two characters removed then base64 encoded
|
||||
|
||||
This gives us good coverage across the board for secrets and secrets with a prefix (i.e. `base64($user:$pass)`).
|
||||
|
||||
However, we don't have great coverage for cases where the secret has a string appended to it before it is base64 encoded (i.e.: `base64($pass\n))`).
|
||||
|
||||
Most notably we've seen this as a result of user error where a user accidentially appends a newline or space character before encoding their secret in base64.
|
||||
|
||||
## Decision
|
||||
|
||||
### Trim end characters
|
||||
|
||||
We are going to modify all existing base64 encoders to trim information before registering as a secret.
|
||||
We will trim:
|
||||
- `=` from the end of all base64 strings. This is a padding character that contains no information.
|
||||
- Based on the number of `=`'s at the end of a base64 string, a malicious user could predict the length of the original secret modulo 3.
|
||||
- If a user saw `***==`, they would know the secret could be 1,4,7,10... characters.
|
||||
- If a string contains `=` we will also trim the last non-padding character from the base64 secret.
|
||||
- This character can change if a string is appended to the secret before the encoding.
|
||||
|
||||
|
||||
### Register a fourth encoder
|
||||
|
||||
We will also add back in the original base64 encoded secret encoder for four total encoders:
|
||||
- The base64 encoded secret
|
||||
- The base64 encoded secret trimmed
|
||||
- The secret with the first character removed then base64 encoded and trimmed
|
||||
- The secret with the first two characters removed then base64 encoded and trimmed
|
||||
|
||||
This allows us to fully cover the most common scenario where a user base64 encodes their secret and expects the entire thing to be masked.
|
||||
This will result in us only revealing length or bit information when a prefix or suffix is added to a secret before encoding.
|
||||
|
||||
## Consequences
|
||||
|
||||
- In the case where a secret has a prefix or suffix added before base64 encoding, we may now reveal up to 20 bits of information and the length of the original string modulo 3, rather then the original 16 bits and no length information
|
||||
- Secrets with a suffix appended before encoding will now be masked across the board. Previously it was only masked if it was a multiple of 3 characters
|
||||
- Performance will suffer in a neglible way
|
||||
@@ -1,30 +1,17 @@
|
||||
## Features
|
||||
- Expose whether debug is on/off via RUNNER_DEBUG. (#253)
|
||||
- Upload log on runner when worker get killed due to cancellation timeout. (#255)
|
||||
- Update config.sh/cmd --help documentation (#282)
|
||||
- Set http_proxy and related env vars for job/service containers (#304)
|
||||
- Set both http_proxy and HTTP_PROXY env for runner/worker processes. (#298)
|
||||
- Remove runner flow: Change from PAT to "deletion token" in prompt (#225)
|
||||
- Expose github.run_id and github.run_number to action runtime env. (#224)
|
||||
|
||||
## Bugs
|
||||
- Verify runner Windows service hash started successfully after configuration (#236)
|
||||
- Detect source file path in L0 without using env. (#257)
|
||||
- Handle escaped '%' in commands data section (#200)
|
||||
- Allow container to be null/empty during matrix expansion (#266)
|
||||
- Translate problem matcher file to host path (#272)
|
||||
- Change hashFiles() expression function to use @actions/glob. (#268)
|
||||
- Default post-job action's condition to always(). (#293)
|
||||
- Support action.yaml file as action's entry file (#288)
|
||||
- Trace javascript action exit code to debug instead of user logs (#290)
|
||||
- Change prompt message when removing a runner to lines up with GitHub.com UI (#303)
|
||||
- Include step.env as part of env context. (#300)
|
||||
- Update Base64 Encoders to deal with suffixes (#284)
|
||||
- Clean up error messages for container scenarios (#221)
|
||||
- Pick shell from prependpath (#231)
|
||||
|
||||
## Misc
|
||||
- Move .sln file under ./src (#238)
|
||||
- Treat warnings as errors during compile (#249)
|
||||
- Runner code cleanup (#218 #227, #228, #229, #230)
|
||||
- Consume dotnet core 3.1 in runner. (#213)
|
||||
|
||||
## Windows x64
|
||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows
|
||||
We recommend configuring the runner under "<DRIVE>:\actions-runner". This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows
|
||||
```
|
||||
// Create a folder under the drive root
|
||||
mkdir \actions-runner ; cd \actions-runner
|
||||
@@ -32,7 +19,7 @@ mkdir \actions-runner ; cd \actions-runner
|
||||
Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-win-x64-<RUNNER_VERSION>.zip -OutFile actions-runner-win-x64-<RUNNER_VERSION>.zip
|
||||
// Extract the installer
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem ;
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$HOME\Downloads\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
|
||||
```
|
||||
|
||||
## OSX
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<packageSources>
|
||||
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||
<clear />
|
||||
<add key="dotnet-core" value="https://www.myget.org/F/dotnet-core/api/v3/index.json" />
|
||||
<add key="dotnet-buildtools" value="https://www.myget.org/F/dotnet-buildtools/api/v3/index.json" />
|
||||
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
|
||||
@@ -136,12 +136,6 @@ 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 Path
|
||||
|
||||
@@ -46,7 +46,6 @@ 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,7 +22,6 @@ 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
|
||||
@@ -114,11 +113,5 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.Token,
|
||||
description: "Enter runner remove token:",
|
||||
description: "Enter runner deletion token:",
|
||||
defaultValue: string.Empty,
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
@@ -291,7 +291,7 @@ namespace GitHub.Runner.Listener
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
// After read the arg from input commandline args, remove it from Arg dictionary,
|
||||
// This will help if bad arg value passed through CommandLine arg, when ConfigurationManager ask CommandSetting the second time,
|
||||
// This will help if bad arg value passed through CommandLine arg, when ConfigurationManager ask CommandSetting the second time,
|
||||
// It will prompt for input instead of continue use the bad input.
|
||||
_trace.Info($"Remove {name} from Arg dictionary.");
|
||||
RemoveArg(name);
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.OAuth;
|
||||
using GitHub.Services.WebApi;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Runtime.InteropServices;
|
||||
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.Headers;
|
||||
|
||||
namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
@@ -108,7 +109,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
runnerSettings.GitHubUrl = inputUrl;
|
||||
var githubToken = command.GetRunnerRegisterToken();
|
||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken, Constants.RunnerEvent.Register);
|
||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken);
|
||||
runnerSettings.ServerUrl = authResult.TenantUrl;
|
||||
creds = authResult.ToVssCredentials();
|
||||
Trace.Info("cred retrieved via GitHub auth");
|
||||
@@ -276,15 +277,12 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
throw new NotSupportedException("Message queue listen OAuth token.");
|
||||
}
|
||||
|
||||
// Testing agent connection, detect any potential connection issue, like local clock skew that cause OAuth token expired.
|
||||
// Testing agent connection, detect any protential connection issue, like local clock skew that cause OAuth token expired.
|
||||
var credMgr = HostContext.GetService<ICredentialManager>();
|
||||
VssCredentials credential = credMgr.LoadCredentials();
|
||||
try
|
||||
{
|
||||
await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), credential);
|
||||
// ConnectAsync() hits _apis/connectionData which is an anonymous endpoint
|
||||
// Need to hit an authenticate endpoint to trigger OAuth token exchange.
|
||||
await _runnerServer.GetAgentPoolsAsync();
|
||||
_term.WriteSuccessMessage("Runner connection is good");
|
||||
}
|
||||
catch (VssOAuthTokenRequestException ex) when (ex.Message.Contains("Current server time is"))
|
||||
@@ -375,7 +373,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
else
|
||||
{
|
||||
var githubToken = command.GetRunnerDeletionToken();
|
||||
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken, Constants.RunnerEvent.Remove);
|
||||
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken);
|
||||
creds = authResult.ToVssCredentials();
|
||||
Trace.Info("cred retrieved via GitHub auth");
|
||||
}
|
||||
@@ -519,23 +517,17 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent)
|
||||
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken)
|
||||
{
|
||||
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
||||
var githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/actions/runner-registration";
|
||||
var gitHubUrl = new UriBuilder(githubUrl);
|
||||
var githubApiUrl = $"https://api.{gitHubUrl.Host}/repos/{gitHubUrl.Path.Trim('/')}/actions-runners/registration";
|
||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||
using (var httpClient = new HttpClient(httpClientHandler))
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("RemoteAuth", githubToken);
|
||||
httpClient.DefaultRequestHeaders.UserAgent.Add(HostContext.UserAgent);
|
||||
|
||||
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"));
|
||||
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.shuri-preview+json"));
|
||||
var response = await httpClient.PostAsync(githubApiUrl, new StringContent("", null, "application/json"));
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace GitHub.Runner.Sdk
|
||||
private string _httpsProxyAddress;
|
||||
private string _httpsProxyUsername;
|
||||
private string _httpsProxyPassword;
|
||||
private string _noProxyString;
|
||||
|
||||
private readonly List<ByPassInfo> _noProxyList = new List<ByPassInfo>();
|
||||
private readonly HashSet<string> _noProxyUnique = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -34,7 +33,6 @@ namespace GitHub.Runner.Sdk
|
||||
public string HttpsProxyAddress => _httpsProxyAddress;
|
||||
public string HttpsProxyUsername => _httpsProxyUsername;
|
||||
public string HttpsProxyPassword => _httpsProxyPassword;
|
||||
public string NoProxyString => _noProxyString;
|
||||
|
||||
public List<ByPassInfo> NoProxyList => _noProxyList;
|
||||
|
||||
@@ -74,8 +72,8 @@ namespace GitHub.Runner.Sdk
|
||||
_httpProxyAddress = proxyHttpUri.AbsoluteUri;
|
||||
|
||||
// Set both environment variables since there are tools support both casing (curl, wget) and tools support only one casing (docker)
|
||||
Environment.SetEnvironmentVariable("HTTP_PROXY", _httpProxyAddress);
|
||||
Environment.SetEnvironmentVariable("http_proxy", _httpProxyAddress);
|
||||
Environment.SetEnvironmentVariable("HTTP_PROXY", _httpProxyAddress);
|
||||
|
||||
// the proxy url looks like http://[user:pass@]127.0.0.1:8888
|
||||
var userInfo = Uri.UnescapeDataString(proxyHttpUri.UserInfo).Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
@@ -104,8 +102,8 @@ namespace GitHub.Runner.Sdk
|
||||
_httpsProxyAddress = proxyHttpsUri.AbsoluteUri;
|
||||
|
||||
// Set both environment variables since there are tools support both casing (curl, wget) and tools support only one casing (docker)
|
||||
Environment.SetEnvironmentVariable("HTTPS_PROXY", _httpsProxyAddress);
|
||||
Environment.SetEnvironmentVariable("https_proxy", _httpsProxyAddress);
|
||||
Environment.SetEnvironmentVariable("HTTPS_PROXY", _httpsProxyAddress);
|
||||
|
||||
// the proxy url looks like http://[user:pass@]127.0.0.1:8888
|
||||
var userInfo = Uri.UnescapeDataString(proxyHttpsUri.UserInfo).Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
@@ -131,11 +129,9 @@ namespace GitHub.Runner.Sdk
|
||||
|
||||
if (!string.IsNullOrEmpty(noProxyList))
|
||||
{
|
||||
_noProxyString = noProxyList;
|
||||
|
||||
// Set both environment variables since there are tools support both casing (curl, wget) and tools support only one casing (docker)
|
||||
Environment.SetEnvironmentVariable("NO_PROXY", noProxyList);
|
||||
Environment.SetEnvironmentVariable("no_proxy", noProxyList);
|
||||
Environment.SetEnvironmentVariable("NO_PROXY", noProxyList);
|
||||
|
||||
var noProxyListSplit = noProxyList.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string noProxy in noProxyListSplit)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -16,14 +15,14 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
void EnablePluginInternalCommand();
|
||||
void DisablePluginInternalCommand();
|
||||
bool TryProcessCommand(IExecutionContext context, string input, ContainerInfo container);
|
||||
bool TryProcessCommand(IExecutionContext context, string input);
|
||||
}
|
||||
|
||||
public sealed class ActionCommandManager : RunnerService, IActionCommandManager
|
||||
{
|
||||
private const string _stopCommand = "stop-commands";
|
||||
private readonly Dictionary<string, IActionCommandExtension> _commandExtensions = new Dictionary<string, IActionCommandExtension>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly HashSet<string> _registeredCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private HashSet<string> _registeredCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object _commandSerializeLock = new object();
|
||||
private bool _stopProcessCommand = false;
|
||||
private string _stopToken = null;
|
||||
@@ -59,7 +58,7 @@ namespace GitHub.Runner.Worker
|
||||
_registeredCommands.Remove("internal-set-repo-path");
|
||||
}
|
||||
|
||||
public bool TryProcessCommand(IExecutionContext context, string input, ContainerInfo container)
|
||||
public bool TryProcessCommand(IExecutionContext context, string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
@@ -115,7 +114,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
try
|
||||
{
|
||||
extension.ProcessCommand(context, input, actionCommand, container);
|
||||
extension.ProcessCommand(context, input, actionCommand);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -141,7 +140,7 @@ namespace GitHub.Runner.Worker
|
||||
string Command { get; }
|
||||
bool OmitEcho { get; }
|
||||
|
||||
void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container);
|
||||
void ProcessCommand(IExecutionContext context, string line, ActionCommand command);
|
||||
}
|
||||
|
||||
public sealed class InternalPluginSetRepoPathCommandExtension : RunnerService, IActionCommandExtension
|
||||
@@ -151,7 +150,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
if (!command.Properties.TryGetValue(SetRepoPathCommandProperties.repoFullName, out string repoFullName) || string.IsNullOrEmpty(repoFullName))
|
||||
{
|
||||
@@ -181,7 +180,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
if (!command.Properties.TryGetValue(SetEnvCommandProperties.Name, out string envName) || string.IsNullOrEmpty(envName))
|
||||
{
|
||||
@@ -206,7 +205,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName))
|
||||
{
|
||||
@@ -230,7 +229,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName))
|
||||
{
|
||||
@@ -254,7 +253,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(command.Data))
|
||||
{
|
||||
@@ -280,7 +279,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
ArgUtil.NotNullOrEmpty(command.Data, "path");
|
||||
context.PrependPath.RemoveAll(x => string.Equals(x, command.Data, StringComparison.CurrentCulture));
|
||||
@@ -288,30 +287,6 @@ 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";
|
||||
@@ -319,7 +294,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
var file = command.Data;
|
||||
|
||||
@@ -331,9 +306,9 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
|
||||
// Translate file path back from container path
|
||||
if (container != null)
|
||||
if (context.Container != null)
|
||||
{
|
||||
file = container.TranslateToHostPath(file);
|
||||
file = context.Container.TranslateToHostPath(file);
|
||||
}
|
||||
|
||||
// Root the path
|
||||
@@ -366,7 +341,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
command.Properties.TryGetValue(RemoveMatcherCommandProperties.Owner, out string owner);
|
||||
var file = command.Data;
|
||||
@@ -394,9 +369,9 @@ namespace GitHub.Runner.Worker
|
||||
else
|
||||
{
|
||||
// Translate file path back from container path
|
||||
if (container != null)
|
||||
if (context.Container != null)
|
||||
{
|
||||
file = container.TranslateToHostPath(file);
|
||||
file = context.Container.TranslateToHostPath(file);
|
||||
}
|
||||
|
||||
// Root the path
|
||||
@@ -434,7 +409,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command)
|
||||
{
|
||||
context.Debug(command.Data);
|
||||
}
|
||||
@@ -462,7 +437,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command)
|
||||
{
|
||||
command.Properties.TryGetValue(IssueCommandProperties.File, out string file);
|
||||
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
|
||||
@@ -479,10 +454,10 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
issue.Category = "Code";
|
||||
|
||||
if (container != null)
|
||||
if (context.Container != null)
|
||||
{
|
||||
// Translate file path back from container path
|
||||
file = container.TranslateToHostPath(file);
|
||||
file = context.Container.TranslateToHostPath(file);
|
||||
command.Properties[IssueCommandProperties.File] = file;
|
||||
}
|
||||
|
||||
@@ -542,7 +517,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
var data = this is GroupCommandExtension ? command.Data : string.Empty;
|
||||
context.Output($"##[{Command}]{data}");
|
||||
@@ -556,7 +531,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
|
||||
{
|
||||
ArgUtil.NotNullOrEmpty(command.Data, "value");
|
||||
|
||||
|
||||
@@ -21,24 +21,11 @@ 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<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps);
|
||||
Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps);
|
||||
Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action);
|
||||
}
|
||||
|
||||
@@ -52,7 +39,7 @@ namespace GitHub.Runner.Worker
|
||||
private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>();
|
||||
|
||||
public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers;
|
||||
public async Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
|
||||
public async Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
|
||||
{
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
ArgUtil.NotNull(steps, nameof(steps));
|
||||
@@ -62,7 +49,6 @@ 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
|
||||
@@ -125,21 +111,6 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +147,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
#endif
|
||||
|
||||
return new PrepareResult(containerSetupSteps, preStepTracker);
|
||||
return containerSetupSteps;
|
||||
}
|
||||
|
||||
public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action)
|
||||
@@ -191,7 +162,6 @@ 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.");
|
||||
@@ -210,7 +180,6 @@ 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))
|
||||
{
|
||||
@@ -270,11 +239,6 @@ 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}.");
|
||||
@@ -294,7 +258,6 @@ 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"}.");
|
||||
}
|
||||
@@ -809,8 +772,6 @@ 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; }
|
||||
@@ -821,8 +782,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public MappingToken Environment { get; set; }
|
||||
|
||||
public string Init { get; set; }
|
||||
|
||||
public string Cleanup { get; set; }
|
||||
}
|
||||
|
||||
@@ -830,14 +789,10 @@ 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; }
|
||||
}
|
||||
|
||||
@@ -845,9 +800,6 @@ 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; }
|
||||
@@ -858,20 +810,16 @@ 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
|
||||
@@ -879,12 +827,6 @@ namespace GitHub.Runner.Worker
|
||||
get { return _cleanupCondition; }
|
||||
set { _cleanupCondition = value; }
|
||||
}
|
||||
|
||||
public string InitCondition
|
||||
{
|
||||
get { return _initCondition; }
|
||||
set { _initCondition = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ContainerSetupInfo
|
||||
|
||||
@@ -280,9 +280,6 @@ 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);
|
||||
@@ -321,15 +318,6 @@ 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;
|
||||
@@ -352,8 +340,6 @@ 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()"
|
||||
};
|
||||
@@ -370,8 +356,6 @@ 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,7 +18,6 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public enum ActionRunStage
|
||||
{
|
||||
Pre,
|
||||
Main,
|
||||
Post,
|
||||
}
|
||||
@@ -82,18 +81,20 @@ 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.Pre || Stage == ActionRunStage.Main))
|
||||
if (handlerData.HasCleanup && Stage == ActionRunStage.Main)
|
||||
{
|
||||
string postDisplayName = $"Post {this.DisplayName}";
|
||||
string postDisplayName = null;
|
||||
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 pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
|
||||
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
|
||||
@@ -107,7 +108,7 @@ namespace GitHub.Runner.Worker
|
||||
actionRunner.Condition = handlerData.CleanupCondition;
|
||||
actionRunner.DisplayName = postDisplayName;
|
||||
|
||||
ExecutionContext.RegisterPostJobStep(actionRunner);
|
||||
ExecutionContext.RegisterPostJobStep($"{actionRunner.Action.Name}_post", actionRunner);
|
||||
}
|
||||
|
||||
IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>();
|
||||
@@ -178,15 +179,17 @@ namespace GitHub.Runner.Worker
|
||||
ExecutionContext.Debug("Loading env");
|
||||
var environment = new Dictionary<String, String>(VarUtil.EnvironmentVariableKeyComparer);
|
||||
|
||||
#if OS_WINDOWS
|
||||
var envContext = ExecutionContext.ExpressionValues["env"] as DictionaryContextData;
|
||||
#else
|
||||
var envContext = ExecutionContext.ExpressionValues["env"] as CaseSensitiveDictionaryContextData;
|
||||
#endif
|
||||
// Apply environment from env context, env context contains job level env and action's evn block
|
||||
foreach (var env in envContext)
|
||||
// Apply environment set using ##[set-env] first since these are job level env
|
||||
foreach (var env in ExecutionContext.EnvironmentVariables)
|
||||
{
|
||||
environment[env.Key] = env.Value.ToString();
|
||||
environment[env.Key] = env.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
// Apply action's env block later.
|
||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(Action.Environment, ExecutionContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer);
|
||||
foreach (var env in actionEnvironment)
|
||||
{
|
||||
environment[env.Key] = env.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
// Apply action's intra-action state at last
|
||||
|
||||
@@ -63,8 +63,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
UserMountVolumes[volume] = volume;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateWebProxyEnv(hostContext.WebProxy);
|
||||
}
|
||||
|
||||
public string ContainerId { get; set; }
|
||||
@@ -224,26 +222,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
{
|
||||
_pathMappings.Insert(0, new PathMapping(hostCommonPath, containerCommonPath));
|
||||
}
|
||||
|
||||
private void UpdateWebProxyEnv(RunnerWebProxy webProxy)
|
||||
{
|
||||
// Set common forms of proxy variables if configured in Runner and not set directly by container.env
|
||||
if (!String.IsNullOrEmpty(webProxy.HttpProxyAddress))
|
||||
{
|
||||
ContainerEnvironmentVariables.TryAdd("HTTP_PROXY", webProxy.HttpProxyAddress);
|
||||
ContainerEnvironmentVariables.TryAdd("http_proxy", webProxy.HttpProxyAddress);
|
||||
}
|
||||
if (!String.IsNullOrEmpty(webProxy.HttpsProxyAddress))
|
||||
{
|
||||
ContainerEnvironmentVariables.TryAdd("HTTPS_PROXY", webProxy.HttpsProxyAddress);
|
||||
ContainerEnvironmentVariables.TryAdd("https_proxy", webProxy.HttpsProxyAddress);
|
||||
}
|
||||
if (!String.IsNullOrEmpty(webProxy.NoProxyString))
|
||||
{
|
||||
ContainerEnvironmentVariables.TryAdd("NO_PROXY", webProxy.NoProxyString);
|
||||
ContainerEnvironmentVariables.TryAdd("no_proxy", webProxy.NoProxyString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MountVolume
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace GitHub.Runner.Worker
|
||||
data: data);
|
||||
|
||||
executionContext.Debug($"Register post job cleanup for stopping/deleting containers.");
|
||||
executionContext.RegisterPostJobStep(postJobStep);
|
||||
executionContext.RegisterPostJobStep(nameof(StopContainersAsync), postJobStep);
|
||||
|
||||
// Check whether we are inside a container.
|
||||
// Our container feature requires to map working directory from host to the container.
|
||||
|
||||
@@ -21,7 +21,6 @@ using System.Collections;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -99,9 +98,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// others
|
||||
void ForceTaskComplete();
|
||||
void RegisterPostJobStep(IStep step);
|
||||
Task<string> GetGitHubToken();
|
||||
Task UpdateGitHubTokenInContext();
|
||||
void RegisterPostJobStep(string refName, IStep step);
|
||||
}
|
||||
|
||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||
@@ -113,7 +110,6 @@ 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;
|
||||
|
||||
@@ -134,13 +130,6 @@ 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; }
|
||||
@@ -165,9 +154,6 @@ 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; }
|
||||
|
||||
|
||||
@@ -253,68 +239,9 @@ namespace GitHub.Runner.Worker
|
||||
});
|
||||
}
|
||||
|
||||
public Task<string> GetGitHubToken()
|
||||
public void RegisterPostJobStep(string refName, IStep step)
|
||||
{
|
||||
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);
|
||||
step.ExecutionContext = Root.CreatePostChild(step.DisplayName, refName, IntraActionState);
|
||||
Root.PostJobSteps.Push(step);
|
||||
}
|
||||
|
||||
@@ -681,7 +608,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
ExpressionValues["env"] = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
_githubTokenExpireTimer.Start();
|
||||
|
||||
// Prepend Path
|
||||
PrependPath = new List<string>();
|
||||
@@ -692,14 +618,6 @@ 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,
|
||||
@@ -900,7 +818,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
private IExecutionContext CreatePostChild(string displayName, string scopeName, string contextName, Dictionary<string, string> intraActionState)
|
||||
private IExecutionContext CreatePostChild(string displayName, string refName, Dictionary<string, string> intraActionState)
|
||||
{
|
||||
if (!_expandedForPostJob)
|
||||
{
|
||||
@@ -909,8 +827,7 @@ namespace GitHub.Runner.Worker
|
||||
_childTimelineRecordOrder = _childTimelineRecordOrder * 2;
|
||||
}
|
||||
|
||||
var newGuid = Guid.NewGuid();
|
||||
return CreateChild(newGuid, displayName, newGuid.ToString("N"), scopeName, contextName, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count);
|
||||
return CreateChild(Guid.NewGuid(), displayName, refName, null, null, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,10 +82,6 @@ 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,10 +60,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
target = Data.Script;
|
||||
}
|
||||
else if (stage == ActionRunStage.Pre)
|
||||
{
|
||||
target = Data.Init;
|
||||
}
|
||||
else if (stage == ActionRunStage.Post)
|
||||
{
|
||||
target = Data.Cleanup;
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
// This does not need to be inside of a critical section.
|
||||
// The logging queues and command handlers are thread-safe.
|
||||
if (_commandManager.TryProcessCommand(_executionContext, line, _container))
|
||||
if (_commandManager.TryProcessCommand(_executionContext, line))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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 prepareResult = await actionManager.PrepareActionsAsync(context, message.Steps);
|
||||
preJobSteps.AddRange(prepareResult.ContainerSetupSteps);
|
||||
var prepareSteps = await actionManager.PrepareActionsAsync(context, message.Steps);
|
||||
preJobSteps.AddRange(prepareSteps);
|
||||
|
||||
// Add start-container steps, record and stop-container steps
|
||||
if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0)
|
||||
@@ -158,23 +158,9 @@ 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)
|
||||
{
|
||||
@@ -185,12 +171,6 @@ 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
|
||||
@@ -199,14 +179,7 @@ namespace GitHub.Runner.Worker
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||
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);
|
||||
}
|
||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,40 +75,16 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Start
|
||||
step.ExecutionContext.Start();
|
||||
await step.ExecutionContext.UpdateGitHubTokenInContext();
|
||||
|
||||
// Set GITHUB_ACTION
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);
|
||||
}
|
||||
|
||||
// Initialize scope
|
||||
if (InitializeScope(step, scopeInputs))
|
||||
{
|
||||
// Populate env context for each step
|
||||
Trace.Info("Initialize Env context for step");
|
||||
#if OS_WINDOWS
|
||||
var envContext = new DictionaryContextData();
|
||||
#else
|
||||
var envContext = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
foreach (var pair in step.ExecutionContext.EnvironmentVariables)
|
||||
{
|
||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||
}
|
||||
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
// Set GITHUB_ACTION
|
||||
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);
|
||||
|
||||
// Evaluate and merge action's env block to env context
|
||||
var templateTrace = step.ExecutionContext.ToTemplateTraceWriter();
|
||||
var schema = new PipelineTemplateSchemaFactory().CreateSchema();
|
||||
var templateEvaluator = new PipelineTemplateEvaluator(templateTrace, schema);
|
||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer);
|
||||
foreach (var env in actionEnvironment)
|
||||
{
|
||||
envContext[env.Key] = new StringContextData(env.Value ?? string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
var expressionManager = HostContext.GetService<IExpressionManager>();
|
||||
try
|
||||
{
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
"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"
|
||||
}
|
||||
@@ -69,8 +67,6 @@
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -162,8 +162,8 @@ namespace GitHub.Services.Common
|
||||
}
|
||||
|
||||
IssuedToken token = null;
|
||||
IssuedTokenProvider provider;
|
||||
if (this.Credentials.TryGetTokenProvider(request.RequestUri, out provider))
|
||||
IssuedTokenProvider provider = null;
|
||||
if (this.Credentials != null && this.Credentials.TryGetTokenProvider(request.RequestUri, out provider))
|
||||
{
|
||||
token = provider.CurrentToken;
|
||||
}
|
||||
@@ -227,7 +227,7 @@ namespace GitHub.Services.Common
|
||||
|
||||
responseWrapper = new HttpResponseMessageWrapper(response);
|
||||
|
||||
if (!this.Credentials.IsAuthenticationChallenge(responseWrapper))
|
||||
if (this.Credentials != null && !this.Credentials.IsAuthenticationChallenge(responseWrapper))
|
||||
{
|
||||
// Validate the token after it has been successfully authenticated with the server.
|
||||
if (provider != null)
|
||||
@@ -259,7 +259,10 @@ namespace GitHub.Services.Common
|
||||
}
|
||||
|
||||
// Ensure we have an appropriate token provider for the current challenge
|
||||
provider = this.Credentials.CreateTokenProvider(request.RequestUri, responseWrapper, token);
|
||||
if (this.Credentials != null)
|
||||
{
|
||||
provider = this.Credentials.CreateTokenProvider(request.RequestUri, responseWrapper, token);
|
||||
}
|
||||
|
||||
// Make sure we don't invoke the provider in an invalid state
|
||||
if (provider == null)
|
||||
@@ -308,7 +311,7 @@ namespace GitHub.Services.Common
|
||||
|
||||
// We're out of retries and the response was an auth challenge -- then the request was unauthorized
|
||||
// and we will throw a strongly-typed exception with a friendly error message.
|
||||
if (!succeeded && response != null && this.Credentials.IsAuthenticationChallenge(responseWrapper))
|
||||
if (!succeeded && response != null && (this.Credentials != null && this.Credentials.IsAuthenticationChallenge(responseWrapper)))
|
||||
{
|
||||
String message = null;
|
||||
IEnumerable<String> serviceError;
|
||||
|
||||
@@ -317,40 +317,5 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,19 +35,6 @@ namespace GitHub.DistributedTask.Pipelines.ContextData
|
||||
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(DictionaryContextData)}' was expected.");
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static CaseSensitiveDictionaryContextData AssertCaseSensitiveDictionary(
|
||||
this PipelineContextData value,
|
||||
String objectDescription)
|
||||
{
|
||||
if (value is CaseSensitiveDictionaryContextData dictionary)
|
||||
{
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(CaseSensitiveDictionaryContextData)}' was expected.");
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static StringContextData AssertString(
|
||||
this PipelineContextData value,
|
||||
|
||||
@@ -4,17 +4,6 @@ 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
|
||||
{
|
||||
|
||||
@@ -189,5 +189,11 @@ namespace GitHub.Services.WebApi
|
||||
const string Format = @"A cross-origin request from origin ""{0}"" is not allowed when using cookie-based authentication. An authentication token needs to be provided in the Authorization header of the request.";
|
||||
return string.Format(CultureInfo.CurrentCulture, Format, arg0);
|
||||
}
|
||||
|
||||
public static string UnknownEntityType(object arg0)
|
||||
{
|
||||
const string Format = @"Unknown entityType {0}. Cannot parse.";
|
||||
return string.Format(CultureInfo.CurrentCulture, Format, arg0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using GitHub.Services.WebApi;
|
||||
using GitHub.Services.WebApi.Jwt;
|
||||
|
||||
namespace GitHub.Services.DelegatedAuthorization
|
||||
{
|
||||
[DataContract]
|
||||
[ClientIncludeModel]
|
||||
public class AccessTokenResult
|
||||
{
|
||||
[DataMember]
|
||||
public Guid AuthorizationId { get; set; }
|
||||
[DataMember]
|
||||
public JsonWebToken AccessToken { get; set; }
|
||||
[DataMember]
|
||||
public string TokenType { get; set; }
|
||||
[DataMember]
|
||||
public DateTime ValidTo { get; set; }
|
||||
[DataMember]
|
||||
public RefreshTokenGrant RefreshToken { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public TokenError AccessTokenError { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public bool HasError => AccessTokenError != TokenError.None;
|
||||
|
||||
[DataMember]
|
||||
public string ErrorDescription { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GitHub.Services.DelegatedAuthorization
|
||||
{
|
||||
[KnownType(typeof(RefreshTokenGrant))]
|
||||
[KnownType(typeof(JwtBearerAuthorizationGrant))]
|
||||
[JsonConverter(typeof(AuthorizationGrantJsonConverter))]
|
||||
public abstract class AuthorizationGrant
|
||||
{
|
||||
public AuthorizationGrant(GrantType grantType)
|
||||
{
|
||||
if (grantType == GrantType.None)
|
||||
{
|
||||
throw new ArgumentException("Grant type is required.");
|
||||
}
|
||||
|
||||
GrantType = grantType;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public GrantType GrantType { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using GitHub.Services.WebApi;
|
||||
using GitHub.Services.WebApi.Jwt;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
|
||||
namespace GitHub.Services.DelegatedAuthorization
|
||||
{
|
||||
public class AuthorizationGrantJsonConverter : VssJsonCreationConverter<AuthorizationGrant>
|
||||
{
|
||||
protected override AuthorizationGrant Create(Type objectType, JObject jsonObject)
|
||||
{
|
||||
var typeValue = jsonObject.GetValue(nameof(AuthorizationGrant.GrantType), StringComparison.OrdinalIgnoreCase);
|
||||
if (typeValue == null)
|
||||
{
|
||||
throw new ArgumentException(WebApiResources.UnknownEntityType(typeValue));
|
||||
}
|
||||
|
||||
GrantType grantType;
|
||||
if (typeValue.Type == JTokenType.Integer)
|
||||
{
|
||||
grantType = (GrantType)(Int32)typeValue;
|
||||
}
|
||||
else if (typeValue.Type != JTokenType.String || !Enum.TryParse((String)typeValue, out grantType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
AuthorizationGrant authorizationGrant = null;
|
||||
var jwtObject = jsonObject.GetValue("jwt");
|
||||
if (jwtObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonWebToken jwt = JsonWebToken.Create(jwtObject.ToString());
|
||||
switch (grantType)
|
||||
{
|
||||
case GrantType.JwtBearer:
|
||||
authorizationGrant = new JwtBearerAuthorizationGrant(jwt);
|
||||
break;
|
||||
|
||||
case GrantType.RefreshToken:
|
||||
authorizationGrant = new RefreshTokenGrant(jwt);
|
||||
break;
|
||||
}
|
||||
|
||||
return authorizationGrant;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace GitHub.Services.DelegatedAuthorization
|
||||
{
|
||||
public enum GrantType
|
||||
{
|
||||
None = 0,
|
||||
JwtBearer = 1,
|
||||
RefreshToken = 2,
|
||||
Implicit = 3,
|
||||
ClientCredentials = 4,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using GitHub.Services.WebApi.Jwt;
|
||||
|
||||
namespace GitHub.Services.DelegatedAuthorization
|
||||
{
|
||||
public class JwtBearerAuthorizationGrant : AuthorizationGrant
|
||||
{
|
||||
public JwtBearerAuthorizationGrant(JsonWebToken jwt)
|
||||
: base(GrantType.JwtBearer)
|
||||
{
|
||||
Jwt = jwt;
|
||||
}
|
||||
|
||||
public JsonWebToken Jwt { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Jwt.EncodedToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using GitHub.Services.WebApi.Jwt;
|
||||
|
||||
namespace GitHub.Services.DelegatedAuthorization
|
||||
{
|
||||
public class RefreshTokenGrant : AuthorizationGrant
|
||||
{
|
||||
public RefreshTokenGrant(JsonWebToken jwt)
|
||||
: base(GrantType.RefreshToken)
|
||||
{
|
||||
Jwt = jwt;
|
||||
}
|
||||
|
||||
public JsonWebToken Jwt { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Jwt.EncodedToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace GitHub.Services.DelegatedAuthorization
|
||||
{
|
||||
public enum TokenError
|
||||
{
|
||||
None,
|
||||
GrantTypeRequired,
|
||||
AuthorizationGrantRequired,
|
||||
ClientSecretRequired,
|
||||
RedirectUriRequired,
|
||||
InvalidAuthorizationGrant,
|
||||
InvalidAuthorizationScopes,
|
||||
InvalidRefreshToken,
|
||||
AuthorizationNotFound,
|
||||
AuthorizationGrantExpired,
|
||||
AccessAlreadyIssued,
|
||||
InvalidRedirectUri,
|
||||
AccessTokenNotFound,
|
||||
InvalidAccessToken,
|
||||
AccessTokenAlreadyRefreshed,
|
||||
InvalidClientSecret,
|
||||
ClientSecretExpired,
|
||||
ServerError,
|
||||
AccessDenied,
|
||||
AccessTokenKeyRequired,
|
||||
InvalidAccessTokenKey,
|
||||
FailedToGetAccessToken,
|
||||
InvalidClientId,
|
||||
InvalidClient,
|
||||
InvalidValidTo,
|
||||
InvalidUserId,
|
||||
FailedToIssueAccessToken,
|
||||
AuthorizationGrantScopeMissing,
|
||||
InvalidPublicAccessTokenKey,
|
||||
InvalidPublicAccessToken,
|
||||
/* Deprecated */
|
||||
PublicFeatureFlagNotEnabled,
|
||||
SSHPolicyDisabled
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace GitHub.Services.Tokens
|
||||
{
|
||||
public class GrantTokenSecretPair
|
||||
{
|
||||
public string GrantToken { get; set; }
|
||||
public string ClientSecret { get; set; }
|
||||
}
|
||||
}
|
||||
94
src/Sdk/WebApi/WebApi/HttpClients/TokenOauth2HttpClient.cs
Normal file
94
src/Sdk/WebApi/WebApi/HttpClients/TokenOauth2HttpClient.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.DelegatedAuthorization;
|
||||
using GitHub.Services.WebApi;
|
||||
|
||||
namespace GitHub.Services.Tokens.WebApi
|
||||
{
|
||||
[ResourceArea(TokenOAuth2ResourceIds.AreaId)]
|
||||
public class TokenOauth2HttpClient : VssHttpClientBase
|
||||
{
|
||||
public TokenOauth2HttpClient(Uri baseUrl, VssCredentials credentials)
|
||||
: base(baseUrl, credentials)
|
||||
{
|
||||
}
|
||||
|
||||
public TokenOauth2HttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings)
|
||||
: base(baseUrl, credentials, settings)
|
||||
{
|
||||
}
|
||||
|
||||
public TokenOauth2HttpClient(Uri baseUrl, VssCredentials credentials, params DelegatingHandler[] handlers)
|
||||
: base(baseUrl, credentials, handlers)
|
||||
{
|
||||
}
|
||||
|
||||
public TokenOauth2HttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings, params DelegatingHandler[] handlers)
|
||||
: base(baseUrl, credentials, settings, handlers)
|
||||
{
|
||||
}
|
||||
|
||||
public TokenOauth2HttpClient(Uri baseUrl, HttpMessageHandler pipeline, bool disposeHandler)
|
||||
: base(baseUrl, pipeline, disposeHandler)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Preview API]
|
||||
/// </summary>
|
||||
/// <param name="tokenSecretPair"></param>
|
||||
/// <param name="grantType"></param>
|
||||
/// <param name="hostId"></param>
|
||||
/// <param name="orgHostId"></param>
|
||||
/// <param name="audience"></param>
|
||||
/// <param name="redirectUri"></param>
|
||||
/// <param name="accessId"></param>
|
||||
/// <param name="userState"></param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
public Task<AccessTokenResult> IssueTokenAsync(
|
||||
GrantTokenSecretPair tokenSecretPair,
|
||||
GrantType grantType,
|
||||
Guid hostId,
|
||||
Guid orgHostId,
|
||||
Uri audience = null,
|
||||
Uri redirectUri = null,
|
||||
Guid? accessId = null,
|
||||
object userState = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HttpMethod httpMethod = new HttpMethod("POST");
|
||||
Guid locationId = new Guid("bbc63806-e448-4e88-8c57-0af77747a323");
|
||||
HttpContent content = new ObjectContent<GrantTokenSecretPair>(tokenSecretPair, new VssJsonMediaTypeFormatter(true));
|
||||
|
||||
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
|
||||
queryParams.Add("grantType", grantType.ToString());
|
||||
queryParams.Add("hostId", hostId.ToString());
|
||||
queryParams.Add("orgHostId", orgHostId.ToString());
|
||||
if (audience != null)
|
||||
{
|
||||
queryParams.Add("audience", audience.ToString());
|
||||
}
|
||||
if (redirectUri != null)
|
||||
{
|
||||
queryParams.Add("redirectUri", redirectUri.ToString());
|
||||
}
|
||||
if (accessId != null)
|
||||
{
|
||||
queryParams.Add("accessId", accessId.Value.ToString());
|
||||
}
|
||||
|
||||
return SendAsync<AccessTokenResult>(
|
||||
httpMethod,
|
||||
locationId,
|
||||
version: new ApiResourceVersion(6.0, 1),
|
||||
queryParameters: queryParams,
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken,
|
||||
content: content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.Common.Diagnostics;
|
||||
using GitHub.Services.Tokens;
|
||||
using GitHub.Services.WebApi;
|
||||
|
||||
namespace GitHub.Services.OAuth
|
||||
@@ -55,45 +56,69 @@ namespace GitHub.Services.OAuth
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
VssTraceActivity traceActivity = VssTraceActivity.Current;
|
||||
using (HttpClient client = new HttpClient(CreateMessageHandler(this.AuthorizationUrl)))
|
||||
using (var tokenClient = new Tokens.WebApi.TokenOauth2HttpClient(new Uri("https://vstoken.actions.githubusercontent.com"), null, CreateMessageHandler(this.AuthorizationUrl)))
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage(HttpMethod.Post, this.AuthorizationUrl);
|
||||
requestMessage.Content = CreateRequestContent(grant, credential, tokenParameters);
|
||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
var parameters = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
||||
(credential as IVssOAuthTokenParameterProvider).SetParameters(parameters);
|
||||
|
||||
if (VssClientHttpRequestSettings.Default.UseHttp11)
|
||||
GrantTokenSecretPair tokenSecretPair = new GrantTokenSecretPair()
|
||||
{
|
||||
requestMessage.Version = HttpVersion.Version11;
|
||||
}
|
||||
ClientSecret = parameters[VssOAuthConstants.ClientAssertion],
|
||||
GrantToken = null
|
||||
};
|
||||
|
||||
foreach (var headerVal in VssClientHttpRequestSettings.Default.UserAgent)
|
||||
{
|
||||
if (!requestMessage.Headers.UserAgent.Contains(headerVal))
|
||||
{
|
||||
requestMessage.Headers.UserAgent.Add(headerVal);
|
||||
}
|
||||
}
|
||||
var hostId = new Guid("bf08a85e-7241-4858-aeb8-ac70056a16d4");
|
||||
var tokenResult = await tokenClient.IssueTokenAsync(tokenSecretPair, DelegatedAuthorization.GrantType.ClientCredentials, hostId, hostId, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
using (var response = await client.SendAsync(requestMessage, cancellationToken: cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
string correlationId = "Unknown";
|
||||
if (response.Headers.TryGetValues("x-ms-request-id", out IEnumerable<string> requestIds))
|
||||
{
|
||||
correlationId = string.Join(",", requestIds);
|
||||
}
|
||||
VssHttpEventSource.Log.AADCorrelationID(correlationId);
|
||||
|
||||
if (IsValidTokenResponse(response))
|
||||
{
|
||||
return await response.Content.ReadAsAsync<VssOAuthTokenResponse>(new[] { m_formatter }, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new VssServiceResponseException(response.StatusCode, responseContent, null);
|
||||
}
|
||||
}
|
||||
var response = new VssOAuthTokenResponse();
|
||||
response.AccessToken = tokenResult.AccessToken.EncodedToken;
|
||||
response.Error = tokenResult.AccessTokenError.ToString();
|
||||
response.ErrorDescription = tokenResult.ErrorDescription;
|
||||
response.RefreshToken = tokenResult.RefreshToken?.Jwt?.EncodedToken;
|
||||
response.Scope = tokenResult.AccessToken.Scopes;
|
||||
response.TokenType = tokenResult.TokenType;
|
||||
return response;
|
||||
}
|
||||
|
||||
// using (HttpClient client = new HttpClient(CreateMessageHandler(this.AuthorizationUrl)))
|
||||
// {
|
||||
// var requestMessage = new HttpRequestMessage(HttpMethod.Post, this.AuthorizationUrl);
|
||||
// requestMessage.Content = CreateRequestContent(grant, credential, tokenParameters);
|
||||
// requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
// if (VssClientHttpRequestSettings.Default.UseHttp11)
|
||||
// {
|
||||
// requestMessage.Version = HttpVersion.Version11;
|
||||
// }
|
||||
|
||||
// foreach (var headerVal in VssClientHttpRequestSettings.Default.UserAgent)
|
||||
// {
|
||||
// if (!requestMessage.Headers.UserAgent.Contains(headerVal))
|
||||
// {
|
||||
// requestMessage.Headers.UserAgent.Add(headerVal);
|
||||
// }
|
||||
// }
|
||||
|
||||
// using (var response = await client.SendAsync(requestMessage, cancellationToken: cancellationToken).ConfigureAwait(false))
|
||||
// {
|
||||
// string correlationId = "Unknown";
|
||||
// if (response.Headers.TryGetValues("x-ms-request-id", out IEnumerable<string> requestIds))
|
||||
// {
|
||||
// correlationId = string.Join(",", requestIds);
|
||||
// }
|
||||
// VssHttpEventSource.Log.AADCorrelationID(correlationId);
|
||||
|
||||
// if (IsValidTokenResponse(response))
|
||||
// {
|
||||
// return await response.Content.ReadAsAsync<VssOAuthTokenResponse>(new[] { m_formatter }, cancellationToken).ConfigureAwait(false);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
// throw new VssServiceResponseException(response.StatusCode, responseContent, null);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private static Boolean IsValidTokenResponse(HttpResponseMessage response)
|
||||
@@ -101,7 +126,7 @@ namespace GitHub.Services.OAuth
|
||||
return response.StatusCode == HttpStatusCode.OK || (response.StatusCode == HttpStatusCode.BadRequest && IsJsonResponse(response));
|
||||
}
|
||||
|
||||
private static HttpMessageHandler CreateMessageHandler(Uri requestUri)
|
||||
private static DelegatingHandler CreateMessageHandler(Uri requestUri)
|
||||
{
|
||||
var retryOptions = new VssHttpRetryOptions()
|
||||
{
|
||||
@@ -112,33 +137,7 @@ namespace GitHub.Services.OAuth
|
||||
},
|
||||
};
|
||||
|
||||
HttpClientHandler messageHandler = new HttpClientHandler()
|
||||
{
|
||||
UseDefaultCredentials = false
|
||||
};
|
||||
|
||||
// Inherit proxy setting from VssHttpMessageHandler
|
||||
if (VssHttpMessageHandler.DefaultWebProxy != null)
|
||||
{
|
||||
messageHandler.Proxy = VssHttpMessageHandler.DefaultWebProxy;
|
||||
messageHandler.UseProxy = true;
|
||||
}
|
||||
|
||||
if (requestUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) &&
|
||||
VssClientHttpRequestSettings.Default.ClientCertificateManager != null &&
|
||||
VssClientHttpRequestSettings.Default.ClientCertificateManager.ClientCertificates != null &&
|
||||
VssClientHttpRequestSettings.Default.ClientCertificateManager.ClientCertificates.Count > 0)
|
||||
{
|
||||
messageHandler.ClientCertificates.AddRange(VssClientHttpRequestSettings.Default.ClientCertificateManager.ClientCertificates);
|
||||
}
|
||||
|
||||
if (requestUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) &&
|
||||
VssClientHttpRequestSettings.Default.ServerCertificateValidationCallback != null)
|
||||
{
|
||||
messageHandler.ServerCertificateCustomValidationCallback = VssClientHttpRequestSettings.Default.ServerCertificateValidationCallback;
|
||||
}
|
||||
|
||||
return new VssHttpRetryMessageHandler(retryOptions, messageHandler);
|
||||
return new VssHttpRetryMessageHandler(retryOptions);
|
||||
}
|
||||
|
||||
private static HttpContent CreateRequestContent(params IVssOAuthTokenParameterProvider[] parameterProviders)
|
||||
|
||||
@@ -40,4 +40,17 @@ namespace GitHub.Services.Location
|
||||
|
||||
public static readonly Guid SpsServiceDefinition = new Guid("{DF5F298A-4E06-4815-A13E-6CE90A37EFA4}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace GitHub.Services.Tokens
|
||||
{
|
||||
public static class TokenOAuth2ResourceIds
|
||||
{
|
||||
public const string AreaName = "tokenoauth2";
|
||||
public const string AreaId = "01c5c153-8bc0-4f07-912a-ec4dc386076d";
|
||||
|
||||
public const string TokenResource = "token";
|
||||
public static readonly Guid Token = new Guid("{bbc63806-e448-4e88-8c57-0af77747a323}");
|
||||
}
|
||||
}
|
||||
@@ -498,7 +498,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
_promptManager
|
||||
.Setup(x => x.ReadValue(
|
||||
Constants.Runner.CommandLine.Args.Token, // argName
|
||||
"Enter runner remove token:", // description
|
||||
"Enter runner deletion token:", // description
|
||||
true, // secret
|
||||
string.Empty, // defaultValue
|
||||
Validators.NonEmptyValidator, // validator
|
||||
|
||||
@@ -175,8 +175,8 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
Assert.True(s.PoolId.Equals(_expectedPoolId));
|
||||
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
||||
|
||||
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
||||
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
||||
// validate GetAgentPoolsAsync gets called once with automation pool type
|
||||
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Once);
|
||||
|
||||
_runnerServer.Verify(x => x.AddAgentAsync(It.IsAny<int>(), It.Is<TaskAgent>(a => a.Labels.Contains("self-hosted") && a.Labels.Contains(VarUtil.OS) && a.Labels.Contains(VarUtil.OSArchitecture))), Times.Once);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
@@ -13,35 +11,47 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
public sealed class ActionCommandManagerL0
|
||||
{
|
||||
private ActionCommandManager _commandManager;
|
||||
private Mock<IExecutionContext> _ec;
|
||||
private Mock<IExtensionManager> _extensionManager;
|
||||
private Mock<IPipelineDirectoryManager> _pipelineDirectoryManager;
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void EnablePluginInternalCommand()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
using (TestHostContext _hc = new TestHostContext(this))
|
||||
{
|
||||
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>()))
|
||||
.Returns((string tag, string line) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
_hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.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", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath"));
|
||||
|
||||
_pipelineDirectoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
|
||||
directoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,29 +60,47 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
[Trait("Category", "Worker")]
|
||||
public void DisablePluginInternalCommand()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
using (TestHostContext _hc = new TestHostContext(this))
|
||||
{
|
||||
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>()))
|
||||
.Returns((string tag, string line) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
_hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.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", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath"));
|
||||
|
||||
_commandManager.DisablePluginInternalCommand();
|
||||
commandManager.DisablePluginInternalCommand();
|
||||
|
||||
Assert.False(_commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath", null));
|
||||
Assert.False(commandManager.TryProcessCommand(_ec.Object, "##[internal-set-repo-path repoFullName=actions/runner;workspaceRepo=true]somepath"));
|
||||
|
||||
_pipelineDirectoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
|
||||
directoryManager.Verify(x => x.UpdateRepositoryDirectory(_ec.Object, "actions/runner", "somepath", true), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,27 +109,42 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
[Trait("Category", "Worker")]
|
||||
public void StopProcessCommand()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
using (TestHostContext _hc = new TestHostContext(this))
|
||||
{
|
||||
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>()))
|
||||
.Returns((string tag, string line) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
_hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.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>());
|
||||
|
||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[stop-commands]stopToken", null));
|
||||
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, "##[set-env name=foo]bar", null));
|
||||
ActionCommandManager commandManager = new ActionCommandManager();
|
||||
commandManager.Initialize(_hc);
|
||||
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "##[stop-commands]stopToken"));
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,29 +153,41 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
[Trait("Category", "Worker")]
|
||||
public void EchoProcessCommand()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
using (TestHostContext _hc = new TestHostContext(this))
|
||||
{
|
||||
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>()))
|
||||
.Returns((string tag, string line) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
_hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
|
||||
_ec.SetupAllProperties();
|
||||
|
||||
ActionCommandManager commandManager = new ActionCommandManager();
|
||||
commandManager.Initialize(_hc);
|
||||
|
||||
Assert.False(_ec.Object.EchoOnActionCommand);
|
||||
|
||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::on", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::on"));
|
||||
Assert.True(_ec.Object.EchoOnActionCommand);
|
||||
|
||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::off", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::off"));
|
||||
Assert.False(_ec.Object.EchoOnActionCommand);
|
||||
|
||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::ON", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::ON"));
|
||||
Assert.True(_ec.Object.EchoOnActionCommand);
|
||||
|
||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::Off ", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::Off "));
|
||||
Assert.False(_ec.Object.EchoOnActionCommand);
|
||||
}
|
||||
}
|
||||
@@ -142,7 +197,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
[Trait("Category", "Worker")]
|
||||
public void EchoProcessCommandDebugOn()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
using (TestHostContext _hc = new TestHostContext(this))
|
||||
{
|
||||
// Set up a few things
|
||||
// 1. Job request message (with ACTIONS_STEP_DEBUG = true)
|
||||
@@ -164,135 +219,84 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
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>();
|
||||
configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings());
|
||||
hc.SetSingleton(configurationStore.Object);
|
||||
_hc.SetSingleton(configurationStore.Object);
|
||||
|
||||
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)
|
||||
var ec = new Runner.Worker.ExecutionContext();
|
||||
ec.Initialize(hc);
|
||||
ec.InitializeJob(jobRequest, System.Threading.CancellationToken.None);
|
||||
_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", null));
|
||||
Assert.False(ec.EchoOnActionCommand);
|
||||
Assert.True(commandManager.TryProcessCommand(_ec, "::echo::off"));
|
||||
Assert.False(_ec.EchoOnActionCommand);
|
||||
|
||||
Assert.True(_commandManager.TryProcessCommand(ec, "::echo::on", null));
|
||||
Assert.True(ec.EchoOnActionCommand);
|
||||
Assert.True(commandManager.TryProcessCommand(_ec, "::echo::on"));
|
||||
Assert.True(_ec.EchoOnActionCommand);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void EchoProcessCommandInvalid()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
using (TestHostContext _hc = new TestHostContext(this))
|
||||
{
|
||||
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>()))
|
||||
.Returns((string tag, string line) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
_hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
|
||||
_ec.SetupAllProperties();
|
||||
|
||||
ActionCommandManager commandManager = new ActionCommandManager();
|
||||
commandManager.Initialize(_hc);
|
||||
|
||||
// Echo commands below are considered "processed", but are invalid
|
||||
// 1. Invalid echo value
|
||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::invalid", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::invalid"));
|
||||
Assert.Equal(TaskResult.Failed, _ec.Object.CommandResult);
|
||||
Assert.False(_ec.Object.EchoOnActionCommand);
|
||||
|
||||
// 2. No value
|
||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::", null));
|
||||
Assert.True(commandManager.TryProcessCommand(_ec.Object, "::echo::"));
|
||||
Assert.Equal(TaskResult.Failed, _ec.Object.CommandResult);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
//Act
|
||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
//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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
//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)).ContainerSetupSteps;
|
||||
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
// node.js based action doesn't need any extra steps to build/pull containers.
|
||||
Assert.True(steps.Count == 0);
|
||||
@@ -629,47 +629,6 @@ 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,52 +63,6 @@ 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")]
|
||||
@@ -155,52 +109,6 @@ 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")]
|
||||
@@ -413,94 +321,6 @@ 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")]
|
||||
|
||||
@@ -314,12 +314,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
githubContext.Add("event", JToken.Parse("{\"foo\":\"bar\"}").ToPipelineContextData());
|
||||
_context.Add("github", githubContext);
|
||||
|
||||
#if OS_WINDOWS
|
||||
_context["env"] = new DictionaryContextData();
|
||||
#else
|
||||
_context["env"] = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(_context);
|
||||
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
|
||||
|
||||
@@ -199,20 +199,20 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
|
||||
var postRunner1 = hc.CreateService<IActionRunner>();
|
||||
postRunner1.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner1.Action = new Pipelines.ActionStep() { 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() { Id = Guid.NewGuid(), Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner2.Action = new Pipelines.ActionStep() { Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } };
|
||||
postRunner2.Stage = ActionRunStage.Post;
|
||||
postRunner2.Condition = "always()";
|
||||
postRunner2.DisplayName = "post2";
|
||||
|
||||
action1.RegisterPostJobStep(postRunner1);
|
||||
action2.RegisterPostJobStep(postRunner2);
|
||||
action1.RegisterPostJobStep("post1", postRunner1);
|
||||
action2.RegisterPostJobStep("post2", 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 PrepareResult(new List<JobExtensionRunner>(), new Dictionary<Guid, IActionRunner>())));
|
||||
.Returns(Task.FromResult(new List<JobExtensionRunner>()));
|
||||
|
||||
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 PrepareResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }, new Dictionary<Guid, IActionRunner>())));
|
||||
.Returns(Task.FromResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }));
|
||||
|
||||
List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);
|
||||
|
||||
|
||||
@@ -973,8 +973,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
});
|
||||
|
||||
_commandManager = new Mock<IActionCommandManager>();
|
||||
_commandManager.Setup(x => x.TryProcessCommand(It.IsAny<IExecutionContext>(), It.IsAny<string>(), It.IsAny<ContainerInfo>()))
|
||||
.Returns((IExecutionContext executionContext, string line, ContainerInfo container) =>
|
||||
_commandManager.Setup(x => x.TryProcessCommand(It.IsAny<IExecutionContext>(), It.IsAny<string>()))
|
||||
.Returns((IExecutionContext executionContext, string line) =>
|
||||
{
|
||||
if (line.IndexOf("##[some-command]") >= 0)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private Mock<IExecutionContext> _ec;
|
||||
private StepsRunner _stepsRunner;
|
||||
private Variables _variables;
|
||||
private Dictionary<string, string> _env;
|
||||
private DictionaryContextData _contexts;
|
||||
private JobContext _jobContext;
|
||||
private StepsContext _stepContext;
|
||||
@@ -33,11 +32,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_variables = new Variables(
|
||||
hostContext: hc,
|
||||
copy: variablesToCopy);
|
||||
_env = new Dictionary<string, string>()
|
||||
{
|
||||
{"env1", "1"},
|
||||
{"test", "github_actions"}
|
||||
};
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
_ec.SetupAllProperties();
|
||||
_ec.Setup(x => x.Variables).Returns(_variables);
|
||||
@@ -70,9 +64,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Arrange.
|
||||
var variableSets = new[]
|
||||
{
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Succeeded, "success() || failure()") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") }
|
||||
new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Succeeded, "success() || failure()") },
|
||||
new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Succeeded, "always()") }
|
||||
};
|
||||
foreach (var variableSet in variableSets)
|
||||
{
|
||||
@@ -102,12 +96,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Arrange.
|
||||
var variableSets = new[]
|
||||
{
|
||||
new[] { CreateStep(hc, TaskResult.Failed, "success()", true), CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(hc, TaskResult.Failed, "success()", true), CreateStep(hc, TaskResult.Succeeded, "success() || failure()") },
|
||||
new[] { CreateStep(hc, TaskResult.Failed, "success()", true), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
new[] { CreateStep(hc, TaskResult.Failed, "success()", true), CreateStep(hc, TaskResult.Failed, "success()", true) },
|
||||
new[] { CreateStep(hc, TaskResult.Failed, "success()", true), CreateStep(hc, TaskResult.Failed, "success() || failure()", true) },
|
||||
new[] { CreateStep(hc, TaskResult.Failed, "success()", true), CreateStep(hc, TaskResult.Failed, "always()", true) }
|
||||
new[] { CreateStep(TaskResult.Failed, "success()", true), CreateStep(TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(TaskResult.Failed, "success()", true), CreateStep(TaskResult.Succeeded, "success() || failure()") },
|
||||
new[] { CreateStep(TaskResult.Failed, "success()", true), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
new[] { CreateStep(TaskResult.Failed, "success()", true), CreateStep(TaskResult.Failed, "success()", true) },
|
||||
new[] { CreateStep(TaskResult.Failed, "success()", true), CreateStep(TaskResult.Failed, "success() || failure()", true) },
|
||||
new[] { CreateStep(TaskResult.Failed, "success()", true), CreateStep(TaskResult.Failed, "always()", true) }
|
||||
};
|
||||
foreach (var variableSet in variableSets)
|
||||
{
|
||||
@@ -139,12 +133,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "success()") },
|
||||
Expected = false,
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "success() || failure()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "success() || failure()") },
|
||||
Expected = true,
|
||||
},
|
||||
};
|
||||
@@ -178,27 +172,27 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
Expected = TaskResult.Succeeded,
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
Expected = TaskResult.Failed,
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
Expected = TaskResult.Failed,
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Failed, "always()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Failed, "always()") },
|
||||
Expected = TaskResult.Failed,
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Failed, "always()", true) },
|
||||
Steps = new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Failed, "always()", true) },
|
||||
Expected = TaskResult.Succeeded,
|
||||
},
|
||||
};
|
||||
@@ -232,47 +226,47 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "success()") },
|
||||
Expected = TaskResult.Failed
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "success() || failure()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "success() || failure()") },
|
||||
Expected = TaskResult.Failed
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
Expected = TaskResult.Failed
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()", continueOnError: true), CreateStep(hc, TaskResult.Failed, "success()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()", continueOnError: true), CreateStep(TaskResult.Failed, "success()") },
|
||||
Expected = TaskResult.Failed
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()", continueOnError: true), CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()", continueOnError: true), CreateStep(TaskResult.Succeeded, "success()") },
|
||||
Expected = TaskResult.Succeeded
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Failed, "success()", continueOnError: true), CreateStep(hc, TaskResult.Failed, "success()", continueOnError: true) },
|
||||
Steps = new[] { CreateStep(TaskResult.Failed, "success()", continueOnError: true), CreateStep(TaskResult.Failed, "success()", continueOnError: true) },
|
||||
Expected = TaskResult.Succeeded
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Succeeded, "success() || failure()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Succeeded, "success() || failure()") },
|
||||
Expected = TaskResult.Succeeded
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Failed, "success()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Failed, "success()") },
|
||||
Expected = TaskResult.Failed
|
||||
},
|
||||
new
|
||||
{
|
||||
Steps = new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
Steps = new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Succeeded, "success()") },
|
||||
Expected = TaskResult.Succeeded
|
||||
},
|
||||
// Abandoned
|
||||
@@ -310,17 +304,17 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
new
|
||||
{
|
||||
Step = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
Step = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "success()") },
|
||||
Expected = false
|
||||
},
|
||||
new
|
||||
{
|
||||
Step = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "success() || failure()") },
|
||||
Step = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "success() || failure()") },
|
||||
Expected = true
|
||||
},
|
||||
new
|
||||
{
|
||||
Step = new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
Step = new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
Expected = true
|
||||
}
|
||||
};
|
||||
@@ -351,9 +345,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Arrange.
|
||||
var variableSets = new[]
|
||||
{
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
new[] { CreateStep(hc, TaskResult.Failed, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") },
|
||||
new[] { CreateStep(hc, TaskResult.Canceled, "success()"), CreateStep(hc, TaskResult.Succeeded, "always()") }
|
||||
new[] { CreateStep(TaskResult.Succeeded, "success()"), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
new[] { CreateStep(TaskResult.Failed, "success()"), CreateStep(TaskResult.Succeeded, "always()") },
|
||||
new[] { CreateStep(TaskResult.Canceled, "success()"), CreateStep(TaskResult.Succeeded, "always()") }
|
||||
};
|
||||
foreach (var variableSet in variableSets)
|
||||
{
|
||||
@@ -387,8 +381,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Arrange.
|
||||
var variableSets = new[]
|
||||
{
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(hc, TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(TaskResult.Succeeded, "success()") },
|
||||
new[] { CreateStep(TaskResult.Succeeded, "success()") },
|
||||
};
|
||||
foreach (var variableSet in variableSets)
|
||||
{
|
||||
@@ -405,134 +399,18 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task StepEnvOverrideJobEnvContext()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var env1 = new MappingToken(null, null, null);
|
||||
env1.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "100"));
|
||||
env1.Add(new StringToken(null, null, null, "env2"), new BasicExpressionToken(null, null, null, "env.test"));
|
||||
var step1 = CreateStep(hc, TaskResult.Succeeded, "success()", env: env1);
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||
|
||||
#if OS_WINDOWS
|
||||
Assert.Equal("100", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("100"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("github_actions"));
|
||||
#else
|
||||
Assert.Equal("100", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("100"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("github_actions"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task PopulateEnvContextForEachStep()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var env1 = new MappingToken(null, null, null);
|
||||
env1.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "100"));
|
||||
env1.Add(new StringToken(null, null, null, "env2"), new BasicExpressionToken(null, null, null, "env.test"));
|
||||
var step1 = CreateStep(hc, TaskResult.Succeeded, "success()", env: env1);
|
||||
|
||||
var env2 = new MappingToken(null, null, null);
|
||||
env2.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "1000"));
|
||||
env2.Add(new StringToken(null, null, null, "env3"), new BasicExpressionToken(null, null, null, "env.test"));
|
||||
var step2 = CreateStep(hc, TaskResult.Succeeded, "success()", env: env2);
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||
#if OS_WINDOWS
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env3"].AssertString("github_actions"));
|
||||
Assert.False(_ec.Object.ExpressionValues["env"].AssertDictionary("env").ContainsKey("env2"));
|
||||
#else
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env3"].AssertString("github_actions"));
|
||||
Assert.False(_ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env").ContainsKey("env2"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task PopulateEnvContextAfterSetupStepsContext()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var env1 = new MappingToken(null, null, null);
|
||||
env1.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "100"));
|
||||
var step1 = CreateStep(hc, TaskResult.Succeeded, "success()", env: env1, name: "foo", setOutput: true);
|
||||
|
||||
var env2 = new MappingToken(null, null, null);
|
||||
env2.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "1000"));
|
||||
env2.Add(new StringToken(null, null, null, "env2"), new BasicExpressionToken(null, null, null, "steps.foo.outputs.test"));
|
||||
var step2 = CreateStep(hc, TaskResult.Succeeded, "success()", env: env2);
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||
#if OS_WINDOWS
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("something", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("something"));
|
||||
#else
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("something", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("something"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private Mock<IActionRunner> CreateStep(TestHostContext hc, TaskResult result, string condition, Boolean continueOnError = false, MappingToken env = null, string name = "Test", bool setOutput = false)
|
||||
private Mock<IStep> CreateStep(TaskResult result, string condition, Boolean continueOnError = false)
|
||||
{
|
||||
// Setup the step.
|
||||
var step = new Mock<IActionRunner>();
|
||||
var step = new Mock<IStep>();
|
||||
step.Setup(x => x.Condition).Returns(condition);
|
||||
step.Setup(x => x.ContinueOnError).Returns(new BooleanToken(null, null, null, continueOnError));
|
||||
step.Setup(x => x.Action)
|
||||
.Returns(new DistributedTask.Pipelines.ActionStep()
|
||||
{
|
||||
Name = name,
|
||||
Id = Guid.NewGuid(),
|
||||
Environment = env
|
||||
});
|
||||
step.Setup(x => x.RunAsync()).Returns(Task.CompletedTask);
|
||||
|
||||
// Setup the step execution context.
|
||||
var stepContext = new Mock<IExecutionContext>();
|
||||
stepContext.SetupAllProperties();
|
||||
stepContext.Setup(x => x.WriteDebug).Returns(true);
|
||||
stepContext.Setup(x => x.Variables).Returns(_variables);
|
||||
stepContext.Setup(x => x.EnvironmentVariables).Returns(_env);
|
||||
stepContext.Setup(x => x.ExpressionValues).Returns(_contexts);
|
||||
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
||||
stepContext.Setup(x => x.StepsContext).Returns(_stepContext);
|
||||
@@ -544,24 +422,13 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
stepContext.Object.Result = r;
|
||||
}
|
||||
});
|
||||
var trace = hc.GetTrace();
|
||||
stepContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { trace.Info($"[{tag}]{message}"); });
|
||||
stepContext.Object.Result = result;
|
||||
step.Setup(x => x.ExecutionContext).Returns(stepContext.Object);
|
||||
|
||||
if (setOutput)
|
||||
{
|
||||
step.Setup(x => x.RunAsync()).Callback(() => { _stepContext.SetOutput(null, name, "test", "something", out string reference); }).Returns(Task.CompletedTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
step.Setup(x => x.RunAsync()).Returns(Task.CompletedTask);
|
||||
}
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
private string FormatSteps(IEnumerable<Mock<IActionRunner>> steps)
|
||||
private string FormatSteps(IEnumerable<Mock<IStep>> steps)
|
||||
{
|
||||
return String.Join(
|
||||
" ; ",
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
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()'
|
||||
@@ -1,26 +0,0 @@
|
||||
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'
|
||||
@@ -1,22 +0,0 @@
|
||||
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()'
|
||||
@@ -1,21 +0,0 @@
|
||||
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'
|
||||
11
src/dev.cmd
11
src/dev.cmd
@@ -1,16 +1,5 @@
|
||||
@setlocal
|
||||
@echo off
|
||||
rem add expected utils to path
|
||||
IF EXIST C:\Program Files\Git\usr\bin (
|
||||
SET PATH=C:\Program Files\Git\usr\bin;%PATH%
|
||||
)
|
||||
IF EXIST C:\Program Files\Git\mingw64\bin (
|
||||
SET PATH=C:\Program Files\Git\mingw64\bin;%PATH%
|
||||
)
|
||||
IF EXIST C:\Program Files\Git\bin (
|
||||
SET PATH=C:\Program Files\Git\bin;%PATH%
|
||||
)
|
||||
|
||||
rem Check if SH_PATH is defined.
|
||||
if defined SH_PATH (
|
||||
goto run
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.165.2
|
||||
2.164.0
|
||||
|
||||
Reference in New Issue
Block a user