Compare commits

..

15 Commits

Author SHA1 Message Date
Thomas Boop
7f6b9e55d6 Update releaseVersion 2022-09-20 14:22:36 -04:00
Thomas Boop
7046439877 fix workflows (#2144) 2022-09-20 14:02:27 -04:00
Thomas Boop
b7fff90e29 M285 Hotfix for container escaping (#2137)
* Fix escaping of docker envs backport

* create as prerelease

* 2.285.2 release notes

Co-authored-by: Nikola Jokic <97525037+nikola-jokic@users.noreply.github.com>
2022-09-20 12:53:30 -04:00
eric sciple
50afba61b4 release 2.285.1 2021-12-06 10:54:57 -06:00
eric sciple
66ee648c13 bump patch version 2021-12-06 10:54:09 -06:00
eric sciple
16e83b0e84 revert node12 version due to fs.copyFileSync hang https://github.com/actions/runner/issues/1536 (#1537) 2021-12-06 10:46:46 -06:00
Ferenc Hammerl
c75a77df66 Update releaseVersion 2021-11-29 17:22:57 +01:00
Ferenc Hammerl
6332f9a42f Prepare for runner 2.285.0 release (#1520) 2021-11-29 16:07:56 +00:00
Tingluo Huang
5b8ff174c6 Add telemetry around runner update process. (#1497)
* Add telemetry around runner update process.

* .

* .

* .
2021-11-22 18:27:57 -05:00
Tingluo Huang
e3e977fd84 Support node.js 16 and bump node.js 12 version. (#1439)
* Support node.js 16 and bump node.js 12 version.

* L0
2021-11-18 15:25:33 -05:00
Ferenc Hammerl
4dc8a09db3 Only execute post for actions that have one (#1481)
* Only execute post for actions that have one

* Revert haspost check

* Remove launch commit

* Remove comment

* Restore whitespace

* Restore wspace
2021-11-18 17:56:13 +01:00
Laura Yu
dcc5d34ad1 Add secret source to start job step (#1411)
* Add secret source to start job step

WIP

* Update to use GetGitHubContext to grab source info

* Update JobExtensionL0.cs

* Update JobExtension.cs

* Update JobExtension.cs
2021-11-17 17:09:38 -05:00
eric sciple
3e34fb10c1 improve telemetry to better diagnose runner configuration issues (#1487) 2021-11-15 13:42:57 -06:00
Tingluo Huang
23a693aa2c Update README.md 2021-11-09 14:19:46 -05:00
Tingluo Huang
eb36db8ff9 Try to delete portable-net45+win8 from all projects (#1470)
* Update Runner.Common.csproj

* Update Runner.Listener.csproj

* Update Runner.PluginHost.csproj

* Update Runner.Plugins.csproj

* Update Runner.Sdk.csproj

* Update Runner.Worker.csproj

* Update Sdk.csproj

* Update Test.csproj
2021-11-09 10:53:53 -05:00
33 changed files with 448 additions and 133 deletions

View File

@@ -37,7 +37,7 @@ jobs:
devScript: ./dev.sh
- runtime: win-x64
os: windows-latest
os: windows-2019
devScript: ./dev
runs-on: ${{ matrix.os }}

View File

@@ -72,7 +72,7 @@ jobs:
devScript: ./dev.sh
- runtime: win-x64
os: windows-latest
os: windows-2019
devScript: ./dev
runs-on: ${{ matrix.os }}
@@ -164,6 +164,7 @@ jobs:
release_name: "v${{ steps.releaseNote.outputs.version }}"
body: |
${{ steps.releaseNote.outputs.note }}
prerelease: true
# Upload release assets
- name: Upload Release Asset (win-x64)

View File

@@ -5,7 +5,7 @@
# GitHub Actions Runner
[![Actions Status](https://github.com/actions/runner/workflows/Runner%20CI/badge.svg)](https://github.com/actions/runner/actions)
[![Runner E2E Test](https://github.com/actions/runner/workflows/Runner%20E2E%20Test/badge.svg)](https://github.com/actions/runner/actions)
[![Runner E2E Test](https://github.com/actions-canary/actions-runner-e2e/actions/workflows/runner_e2etest.yml/badge.svg)](https://github.com/actions-canary/actions-runner-e2e/actions/workflows/runner_e2etest.yml)
The runner is the application that runs a job from a GitHub Actions workflow. It is used by GitHub Actions in the [hosted virtual environments](https://github.com/actions/virtual-environments), or you can [self-host the runner](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners) in your own environment.

View File

@@ -1,19 +1,16 @@
## Features
- Expose GITHUB_REF_* as environment variable (#1314)
- Add arch to runner context (#1372)
- Support Conditional Steps in Composite Actions (#1438)
- Log current runner version in terminal (#1441)
- n/a
## Bugs
- Makes the user keychains available to the service (#847)
- Use Actions Service health and api.github.com endpoints after connection failure on Actions Server and Hosted (#1385)
- Fix an issue where nested local composite actions did not correctly register post steps (#1433)
- Fixed an issue where container environment variables names or values could escape the docker command (#2108)
## Misc
- Cleanup Older versions on MacOS now that we recreate node versions as needed (#1410)
- n/a
## 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.

View File

@@ -1 +1 @@
2.284.0
2.285.2

View File

@@ -4,6 +4,7 @@ PRECACHE=$2
NODE_URL=https://nodejs.org/dist
NODE12_VERSION="12.13.1"
NODE16_VERSION="16.13.0"
get_abs_path() {
# exploits the fact that pwd will print abs path when no args
@@ -126,6 +127,8 @@ function acquireExternalTool() {
if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.exe" node12/bin
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.lib" node12/bin
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.exe" node16/bin
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.lib" node16/bin
if [[ "$PRECACHE" != "" ]]; then
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
fi
@@ -134,18 +137,23 @@ fi
# Download the external tools only for OSX.
if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-darwin-x64.tar.gz" node12 fix_nested_dir
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-x64.tar.gz" node16 fix_nested_dir
fi
# Download the external tools for Linux PACKAGERUNTIMEs.
if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-x64.tar.gz" node12 fix_nested_dir
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE12_VERSION}/alpine/x64/node-${NODE12_VERSION}-alpine-x64.tar.gz" node12_alpine
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-x64.tar.gz" node16 fix_nested_dir
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE16_VERSION}/alpine/x64/node-v${NODE16_VERSION}-alpine-x64.tar.gz" node16_alpine
fi
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-arm64.tar.gz" node12 fix_nested_dir
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-arm64.tar.gz" node16 fix_nested_dir
fi
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-armv7l.tar.gz" node12 fix_nested_dir
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-armv7l.tar.gz" node16 fix_nested_dir
fi

View File

@@ -200,9 +200,17 @@ namespace GitHub.Runner.Common
if (credData != null &&
credData.Data.TryGetValue("clientId", out var clientId))
{
_userAgents.Add(new ProductInfoHeaderValue($"RunnerId", clientId));
_userAgents.Add(new ProductInfoHeaderValue("ClientId", clientId));
}
}
var runnerFile = GetConfigFile(WellKnownConfigFile.Runner);
if (File.Exists(runnerFile))
{
var runnerSettings = IOUtil.LoadObject<RunnerSettings>(runnerFile);
_userAgents.Add(new ProductInfoHeaderValue("RunnerId", runnerSettings.AgentId.ToString(CultureInfo.InvariantCulture)));
_userAgents.Add(new ProductInfoHeaderValue("GroupId", runnerSettings.PoolId.ToString(CultureInfo.InvariantCulture)));
}
}
public string GetDirectory(WellKnownDirectory directory)

View File

@@ -5,7 +5,6 @@
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>

View File

@@ -51,7 +51,7 @@ namespace GitHub.Runner.Common
Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken);
// agent update
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState);
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState, string trace);
}
public sealed class RunnerServer : RunnerService, IRunnerServer
@@ -341,25 +341,10 @@ namespace GitHub.Runner.Common
return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, includeToken, cancellationToken: cancellationToken);
}
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState)
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState, string trace)
{
CheckConnection(RunnerConnectionType.Generic);
return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState);
}
//-----------------------------------------------------------------
// Runner Auth Url
//-----------------------------------------------------------------
public Task<string> GetRunnerAuthUrlAsync(int runnerPoolId, int runnerId)
{
CheckConnection(RunnerConnectionType.MessageQueue);
return _messageTaskAgentClient.GetAgentAuthUrlAsync(runnerPoolId, runnerId);
}
public Task ReportRunnerAuthUrlErrorAsync(int runnerPoolId, int runnerId, string error)
{
CheckConnection(RunnerConnectionType.MessageQueue);
return _messageTaskAgentClient.ReportAgentAuthUrlMigrationErrorAsync(runnerPoolId, runnerId, error);
return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState, trace);
}
}
}

View File

@@ -5,7 +5,6 @@
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>

View File

@@ -13,6 +13,7 @@ using GitHub.Services.WebApi;
using GitHub.Services.Common;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using System.Text;
namespace GitHub.Runner.Listener
{
@@ -63,23 +64,25 @@ namespace GitHub.Runner.Listener
// Print console line that warn user not shutdown runner.
await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner.");
await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner");
await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner", $"RunnerPlatform: {_targetPackage.Platform}");
await DownloadLatestRunner(token);
var downloadTrace = await DownloadLatestRunner(token);
Trace.Info($"Download latest runner and unzip into runner root.");
// wait till all running job finish
await UpdateRunnerUpdateStateAsync("Waiting for current job finish running.");
await UpdateRunnerUpdateStateAsync("Waiting for current job finish running.", downloadTrace);
await jobDispatcher.WaitAsync(token);
Trace.Info($"All running job has exited.");
// We need to keep runner backup around for macOS until we fixed https://github.com/actions/runner/issues/743
// delete runner backup
var stopWatch = Stopwatch.StartNew();
DeletePreviousVersionRunnerBackup(token);
Trace.Info($"Delete old version runner backup.");
stopWatch.Stop();
// generate update script from template
await UpdateRunnerUpdateStateAsync("Generate and execute update script.");
await UpdateRunnerUpdateStateAsync("Generate and execute update script.", $"DeleteRunnerBackupTime: {stopWatch.ElapsedMilliseconds}ms");
string updateScript = GenerateUpdateScript(restartInteractiveRunner);
Trace.Info($"Generate update script into: {updateScript}");
@@ -96,7 +99,7 @@ namespace GitHub.Runner.Listener
invokeScript.Start();
Trace.Info($"Update script start running");
await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should be back online within 10 seconds.");
await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should be back online within 10 seconds.", $"RestartInteractiveRunner: {restartInteractiveRunner}");
return true;
}
@@ -150,8 +153,10 @@ namespace GitHub.Runner.Listener
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async Task DownloadLatestRunner(CancellationToken token)
private async Task<string> DownloadLatestRunner(CancellationToken token)
{
var traceStringBuilder = new StringBuilder();
traceStringBuilder.AppendLine($"DownloadUrl: {_targetPackage.DownloadUrl}");
string latestRunnerDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.UpdateDirectory);
IOUtil.DeleteDirectory(latestRunnerDirectory, token);
Directory.CreateDirectory(latestRunnerDirectory);
@@ -160,6 +165,7 @@ namespace GitHub.Runner.Listener
string archiveFile = null;
bool downloadSucceeded = false;
var stopWatch = Stopwatch.StartNew();
try
{
// Download the runner, using multiple attempts in order to be resilient against any networking/CDN issues
@@ -210,6 +216,7 @@ namespace GitHub.Runner.Listener
try
{
Trace.Info($"Download runner: begin download");
long downloadSize = 0;
//open zip stream in async mode
using (HttpClient httpClient = new HttpClient(HostContext.CreateHttpClientHandler()))
@@ -228,11 +235,16 @@ namespace GitHub.Runner.Listener
//81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
await result.CopyToAsync(fs, 81920, downloadCts.Token);
await fs.FlushAsync(downloadCts.Token);
downloadSize = fs.Length;
}
}
Trace.Info($"Download runner: finished download");
downloadSucceeded = true;
stopWatch.Stop();
traceStringBuilder.AppendLine($"PackageDownloadTime: {stopWatch.ElapsedMilliseconds}ms");
traceStringBuilder.AppendLine($"Attempts: {attempt}");
traceStringBuilder.AppendLine($"PackageSize: {downloadSize / 1024 / 1024}MB");
break;
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
@@ -257,6 +269,7 @@ namespace GitHub.Runner.Listener
throw new TaskCanceledException($"Runner package '{archiveFile}' failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts");
}
stopWatch.Restart();
// If we got this far, we know that we've successfully downloaded the runner package
// Validate Hash Matches if it is provided
using (FileStream stream = File.OpenRead(archiveFile))
@@ -320,7 +333,9 @@ namespace GitHub.Runner.Listener
throw new NotSupportedException($"{archiveFile}");
}
stopWatch.Stop();
Trace.Info($"Finished getting latest runner package at: {latestRunnerDirectory}.");
traceStringBuilder.AppendLine($"PackageExtractTime: {stopWatch.ElapsedMilliseconds}ms");
}
finally
{
@@ -340,6 +355,7 @@ namespace GitHub.Runner.Listener
}
}
stopWatch.Restart();
// copy latest runner into runner root folder
// copy bin from _work/_update -> bin.version under root
string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}");
@@ -365,6 +381,11 @@ namespace GitHub.Runner.Listener
IOUtil.DeleteFile(destination);
file.CopyTo(destination, true);
}
stopWatch.Stop();
traceStringBuilder.AppendLine($"CopyRunnerToRootTime: {stopWatch.ElapsedMilliseconds}ms");
return traceStringBuilder.ToString();
}
private void DeletePreviousVersionRunnerBackup(CancellationToken token)
@@ -484,13 +505,18 @@ namespace GitHub.Runner.Listener
return updateScript;
}
private async Task UpdateRunnerUpdateStateAsync(string currentState)
private async Task UpdateRunnerUpdateStateAsync(string currentState, string trace = "")
{
_terminal.WriteLine(currentState);
if (!string.IsNullOrEmpty(trace))
{
Trace.Info(trace);
}
try
{
await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState);
await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState, trace);
}
catch (VssResourceNotFoundException)
{

View File

@@ -5,7 +5,6 @@
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>

View File

@@ -5,7 +5,6 @@
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>

View File

@@ -5,7 +5,6 @@
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>

View File

@@ -278,7 +278,7 @@ namespace GitHub.Runner.Worker
}
// Clone action so we can modify the condition without affecting the original
var clonedAction = action.Clone() as Pipelines.ActionStep;
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
}
}
}
@@ -443,7 +443,7 @@ namespace GitHub.Runner.Worker
{
for (var i = 0; i < compositeAction.Steps.Count; i++)
{
// Store Id's for later load actions
// Load stored Ids for later load actions
compositeAction.Steps[i].Id = _cachedEmbeddedStepIds[action.Id][i];
if (string.IsNullOrEmpty(executionContext.Global.Variables.Get("DistributedTask.EnableCompositeActions")) && compositeAction.Steps[i].Reference.Type != Pipelines.ActionSourceType.Script)
{
@@ -451,6 +451,16 @@ namespace GitHub.Runner.Worker
}
}
}
else
{
_cachedEmbeddedStepIds[action.Id] = new List<Guid>();
foreach (var compositeStep in compositeAction.Steps)
{
var guid = Guid.NewGuid();
compositeStep.Id = guid;
_cachedEmbeddedStepIds[action.Id].Add(guid);
}
}
}
else
{
@@ -1189,6 +1199,8 @@ namespace GitHub.Runner.Worker
public string Pre { get; set; }
public string Post { get; set; }
public string NodeVersion { get; set; }
}
public sealed class PluginActionExecutionData : ActionExecutionData

View File

@@ -451,7 +451,8 @@ namespace GitHub.Runner.Worker
};
}
}
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase))
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)||
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrEmpty(mainToken?.Value))
{
@@ -461,6 +462,7 @@ namespace GitHub.Runner.Worker
{
return new NodeJSActionExecutionData()
{
NodeVersion = usingToken.Value,
Script = mainToken.Value,
Pre = preToken?.Value,
InitCondition = preIfToken?.Value ?? "always()",
@@ -490,7 +492,7 @@ namespace GitHub.Runner.Worker
}
else
{
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead.");
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker', 'node12' or 'node16' instead.");
}
}
else if (pluginToken != null)

View File

@@ -131,11 +131,11 @@ namespace GitHub.Runner.Worker.Container
{
if (String.IsNullOrEmpty(env.Value))
{
dockerOptions.Add($"-e \"{env.Key}\"");
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
}
else
{
dockerOptions.Add($"-e \"{env.Key}={env.Value.Replace("\"", "\\\"")}\"");
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key, env.Value));
}
}
@@ -202,7 +202,7 @@ namespace GitHub.Runner.Worker.Container
{
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
// the value directly in the command
dockerOptions.Add($"-e {env.Key}");
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
}
// Watermark for GitHub Action environment

View File

@@ -6,6 +6,9 @@ namespace GitHub.Runner.Worker.Container
{
public class DockerUtil
{
private static readonly Regex QuoteEscape = new Regex(@"(\\*)" + "\"", RegexOptions.Compiled);
private static readonly Regex EndOfStringEscape = new Regex(@"(\\+)$", RegexOptions.Compiled);
public static List<PortMapping> ParseDockerPort(IList<string> portMappingLines)
{
const string targetPort = "targetPort";
@@ -17,7 +20,7 @@ namespace GitHub.Runner.Worker.Container
string pattern = $"^(?<{targetPort}>\\d+)/(?<{proto}>\\w+) -> (?<{host}>.+):(?<{hostPort}>\\d+)$";
List<PortMapping> portMappings = new List<PortMapping>();
foreach(var line in portMappingLines)
foreach (var line in portMappingLines)
{
Match m = Regex.Match(line, pattern, RegexOptions.None, TimeSpan.FromSeconds(1));
if (m.Success)
@@ -61,5 +64,44 @@ namespace GitHub.Runner.Worker.Container
}
return "";
}
public static string CreateEscapedOption(string flag, string key)
{
if (String.IsNullOrEmpty(key))
{
return "";
}
return $"{flag} {EscapeString(key)}";
}
public static string CreateEscapedOption(string flag, string key, string value)
{
if (String.IsNullOrEmpty(key))
{
return "";
}
var escapedString = EscapeString($"{key}={value}");
return $"{flag} {escapedString}";
}
private static string EscapeString(string value)
{
if (String.IsNullOrEmpty(value))
{
return "";
}
// Dotnet escaping rules are weird here, we can only escape \ if it precedes a "
// If a double quotation mark follows two or an even number of backslashes, each proceeding backslash pair is replaced with one backslash and the double quotation mark is removed.
// If a double quotation mark follows an odd number of backslashes, including just one, each preceding pair is replaced with one backslash and the remaining backslash is removed; however, in this case the double quotation mark is not removed.
// https://docs.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?redirectedfrom=MSDN&view=net-6.0#remarks
// First, find any \ followed by a " and double the number of \ + 1.
value = QuoteEscape.Replace(value, @"$1$1\" + "\"");
// Next, what if it ends in `\`, it would escape the end quote. So, we need to detect that at the end of the string and perform the same escape
// Luckily, we can just use the $ character with detects the end of string in regex
value = EndOfStringEscape.Replace(value, @"$1$1");
// Finally, wrap it in quotes
return $"\"{value}\"";
}
}
}

View File

@@ -83,7 +83,7 @@ namespace GitHub.Runner.Worker.Handlers
HasPreStep = Data.HasPre,
HasPostStep = Data.HasPost,
IsEmbedded = ExecutionContext.IsEmbedded,
Type = "node12"
Type = Data.NodeVersion
};
ExecutionContext.Root.ActionsStepsTelemetry.Add(telemetry);
}
@@ -99,7 +99,7 @@ namespace GitHub.Runner.Worker.Handlers
workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
}
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext);
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion);
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
// Format the arguments passed to node.

View File

@@ -23,7 +23,7 @@ namespace GitHub.Runner.Worker.Handlers
string ResolvePathForStepHost(string path);
Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext);
Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion);
Task<int> ExecuteAsync(string workingDirectory,
string fileName,
@@ -58,9 +58,9 @@ namespace GitHub.Runner.Worker.Handlers
return path;
}
public Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext)
public Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion)
{
return Task.FromResult<string>("node12");
return Task.FromResult<string>(preferredVersion);
}
public async Task<int> ExecuteAsync(string workingDirectory,
@@ -123,7 +123,7 @@ namespace GitHub.Runner.Worker.Handlers
}
}
public async Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext)
public async Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion)
{
// Best effort to determine a compatible node runtime
// There may be more variation in which libraries are linked than just musl/glibc,
@@ -148,14 +148,14 @@ namespace GitHub.Runner.Worker.Handlers
var msg = $"JavaScript Actions in Alpine containers are only supported on x64 Linux runners. Detected {os} {arch}";
throw new NotSupportedException(msg);
}
nodeExternal = "node12_alpine";
nodeExternal = $"{preferredVersion}_alpine";
executionContext.Debug($"Container distribution is alpine. Running JavaScript Action with external tool: {nodeExternal}");
return nodeExternal;
}
}
}
// Optimistically use the default
nodeExternal = "node12";
nodeExternal = preferredVersion;
executionContext.Debug($"Running JavaScript Action with default external tool: {nodeExternal}");
return nodeExternal;
}
@@ -188,7 +188,7 @@ namespace GitHub.Runner.Worker.Handlers
{
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
// the value directly in the command
dockerCommandArgs.Add($"-e {env.Key}");
dockerCommandArgs.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
}
if (!string.IsNullOrEmpty(PrependPath))
{

View File

@@ -142,6 +142,12 @@ namespace GitHub.Runner.Worker
Trace.Error(ex);
}
var secretSource = context.GetGitHubContext("secret_source");
if (!string.IsNullOrEmpty(secretSource))
{
context.Output($"Secret source: {secretSource}");
}
var repoFullName = context.GetGitHubContext("repository");
ArgUtil.NotNull(repoFullName, nameof(repoFullName));
context.Debug($"Primary repository: {repoFullName}");

View File

@@ -5,7 +5,6 @@
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>

View File

@@ -46,7 +46,7 @@
"runs": {
"one-of": [
"container-runs",
"node12-runs",
"node-runs",
"plugin-runs",
"composite-runs"
]
@@ -80,7 +80,7 @@
"loose-value-type": "string"
}
},
"node12-runs": {
"node-runs": {
"mapping": {
"properties": {
"using": "non-empty-string",
@@ -112,10 +112,10 @@
"item-type": "composite-step"
}
},
"composite-step":{
"composite-step": {
"one-of": [
"run-step",
"uses-step"
"uses-step"
]
},
"run-step": {
@@ -254,4 +254,4 @@
}
}
}
}
}

View File

@@ -768,6 +768,7 @@ namespace GitHub.DistributedTask.WebApi
/// <param name="poolId"></param>
/// <param name="agentId"></param>
/// <param name="currentState"></param>
/// <param name="updateTrace"></param>
/// <param name="userState"></param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -775,6 +776,7 @@ namespace GitHub.DistributedTask.WebApi
int poolId,
int agentId,
string currentState,
string updateTrace,
object userState = null,
CancellationToken cancellationToken = default)
{
@@ -784,6 +786,7 @@ namespace GitHub.DistributedTask.WebApi
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
queryParams.Add("currentState", currentState);
queryParams.Add("updateTrace", updateTrace);
return SendAsync<TaskAgent>(
httpMethod,
@@ -794,65 +797,5 @@ namespace GitHub.DistributedTask.WebApi
userState: userState,
cancellationToken: cancellationToken);
}
/// <summary>
/// [Preview API]
/// </summary>
/// <param name="poolId"></param>
/// <param name="agentId"></param>
/// <param name="userState"></param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
public Task<String> GetAgentAuthUrlAsync(
int poolId,
int agentId,
object userState = null,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("GET");
Guid locationId = new Guid("a82a119c-1e46-44b6-8d75-c82a79cf975b");
object routeValues = new { poolId = poolId, agentId = agentId };
return SendAsync<String>(
httpMethod,
locationId,
routeValues: routeValues,
version: new ApiResourceVersion(6.0, 1),
userState: userState,
cancellationToken: cancellationToken);
}
/// <summary>
/// [Preview API]
/// </summary>
/// <param name="poolId"></param>
/// <param name="agentId"></param>
/// <param name="error"></param>
/// <param name="userState"></param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual async Task ReportAgentAuthUrlMigrationErrorAsync(
int poolId,
int agentId,
string error,
object userState = null,
CancellationToken cancellationToken = default)
{
HttpMethod httpMethod = new HttpMethod("POST");
Guid locationId = new Guid("a82a119c-1e46-44b6-8d75-c82a79cf975b");
object routeValues = new { poolId = poolId, agentId = agentId };
HttpContent content = new ObjectContent<string>(error, new VssJsonMediaTypeFormatter(true));
using (HttpResponseMessage response = await SendAsync(
httpMethod,
locationId,
routeValues: routeValues,
version: new ApiResourceVersion(6.0, 1),
userState: userState,
cancellationToken: cancellationToken,
content: content).ConfigureAwait(false))
{
return;
}
}
}
}

View File

@@ -5,7 +5,6 @@
<OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version>
<DefineConstants>TRACE</DefineConstants>

View File

@@ -144,5 +144,54 @@ namespace GitHub.Runner.Common.Tests.Worker.Container
var actual = DockerUtil.ParseRegistryHostnameFromImageName(input);
Assert.Equal(expected, actual);
}
[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData("", "")]
[InlineData("foo", "foo")]
[InlineData("foo \\ bar", "foo \\ bar")]
[InlineData("foo \\", "foo \\\\")]
[InlineData("foo \\\\", "foo \\\\\\\\")]
[InlineData("foo \\\" bar", "foo \\\\\\\" bar")]
[InlineData("foo \\\\\" bar", "foo \\\\\\\\\\\" bar")]
public void CreateEscapedOption_keyOnly(string input, string escaped)
{
var flag = "--example";
var actual = DockerUtil.CreateEscapedOption(flag, input);
string expected;
if (String.IsNullOrEmpty(input))
{
expected = "";
}
else
{
expected = $"{flag} \"{escaped}\"";
}
Assert.Equal(expected, actual);
}
[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData("foo", "bar", "foo=bar")]
[InlineData("foo\\", "bar", "foo\\=bar")]
[InlineData("foo\\", "bar\\", "foo\\=bar\\\\")]
[InlineData("foo \\","bar \\", "foo \\=bar \\\\")]
public void CreateEscapedOption_keyValue(string keyInput, string valueInput, string escapedString)
{
var flag = "--example";
var actual = DockerUtil.CreateEscapedOption(flag, keyInput, valueInput);
string expected;
if (String.IsNullOrEmpty(keyInput))
{
expected = "";
}
else
{
expected = $"{flag} \"{escapedString}\"";
}
Assert.Equal(expected, actual);
}
}
}

View File

@@ -965,7 +965,7 @@ namespace GitHub.Runner.Common.Tests.Worker
};
//Act
var result = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
var result = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
//Assert
Assert.Equal(1, result.PreStepTracker.Count);
@@ -1288,7 +1288,7 @@ runs:
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void LoadsNodeActionDefinition()
public void LoadsNode12ActionDefinition()
{
try
{
@@ -1344,8 +1344,78 @@ runs:
Assert.True(string.IsNullOrEmpty(inputDefaults["entryPoint"]));
Assert.NotNull(definition.Data.Execution); // execution
Assert.NotNull((definition.Data.Execution as NodeJSActionExecutionData));
Assert.NotNull(definition.Data.Execution as NodeJSActionExecutionData);
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script);
Assert.Equal("node12", (definition.Data.Execution as NodeJSActionExecutionData).NodeVersion);
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void LoadsNode16ActionDefinition()
{
try
{
// Arrange.
Setup();
const string Content = @"
# Container action
name: 'Hello World'
description: 'Greet the world and record the time'
author: 'GitHub'
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: 'node16'
main: 'task.js'
";
Pipelines.ActionStep instance;
string directory;
CreateAction(yamlContent: Content, instance: out instance, directory: out directory);
// Act.
Definition definition = _actionManager.LoadAction(_ec.Object, instance);
// Assert.
Assert.NotNull(definition);
Assert.Equal(directory, definition.Directory);
Assert.NotNull(definition.Data);
Assert.NotNull(definition.Data.Inputs); // inputs
Dictionary<string, string> inputDefaults = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var input in definition.Data.Inputs)
{
var name = input.Key.AssertString("key").Value;
var value = input.Value.AssertScalar("value").ToString();
_hc.GetTrace().Info($"Default: {name} = {value}");
inputDefaults[name] = value;
}
Assert.Equal(2, inputDefaults.Count);
Assert.True(inputDefaults.ContainsKey("greeting"));
Assert.Equal("Hello", inputDefaults["greeting"]);
Assert.True(string.IsNullOrEmpty(inputDefaults["entryPoint"]));
Assert.NotNull(definition.Data.Execution); // execution
Assert.NotNull(definition.Data.Execution as NodeJSActionExecutionData);
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script);
Assert.Equal("node16", (definition.Data.Execution as NodeJSActionExecutionData).NodeVersion);
}
finally
{

View File

@@ -408,6 +408,50 @@ namespace GitHub.Runner.Common.Tests.Worker
var nodeAction = result.Execution as NodeJSActionExecutionData;
Assert.Equal("main.js", nodeAction.Script);
Assert.Equal("node12", nodeAction.NodeVersion);
}
finally
{
Teardown();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_Node16Action()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "node16action.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("node16", nodeAction.NodeVersion);
}
finally
{

View File

@@ -102,6 +102,7 @@ namespace GitHub.Runner.Common.Tests.Worker
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), steps, null, null, null, null);
GitHubContext github = new GitHubContext();
github["repository"] = new Pipelines.ContextData.StringContextData("actions/runner");
github["secret_source"] = new Pipelines.ContextData.StringContextData("Actions");
_message.ContextData.Add("github", github);
hc.SetSingleton(_actionManager.Object);

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Moq;
using Xunit;
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Handlers;
using GitHub.Runner.Worker.Container;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class StepHostL0
{
private Mock<IExecutionContext> _ec;
private Mock<IDockerCommandManager> _dc;
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
{
var hc = new TestHostContext(this, testName);
_ec = new Mock<IExecutionContext>();
_ec.SetupAllProperties();
_ec.Setup(x => x.Global).Returns(new GlobalContext { WriteDebug = true });
var trace = hc.GetTrace();
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { trace.Info($"[{tag}]{message}"); });
_dc = new Mock<IDockerCommandManager>();
hc.SetSingleton(_dc.Object);
return hc;
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task DetermineNodeRuntimeVersionInContainerAsync()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var sh = new ContainerStepHost();
sh.Initialize(hc);
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
.ReturnsAsync(0);
// Act.
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node12");
// Assert.
Assert.Equal("node12", nodeVersion);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task DetermineNodeRuntimeVersionInAlpineContainerAsync()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var sh = new ContainerStepHost();
sh.Initialize(hc);
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
{
output.Add("alpine");
})
.ReturnsAsync(0);
// Act.
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node16");
// Assert.
Assert.Equal("node16_alpine", nodeVersion);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task DetermineNodeRuntimeVersionInUnknowContainerAsync()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var sh = new ContainerStepHost();
sh.Initialize(hc);
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
{
output.Add("github");
})
.ReturnsAsync(0);
// Act.
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node16");
// Assert.
Assert.Equal("node16", nodeVersion);
}
}
}
}

View File

@@ -3,7 +3,6 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603;NU1603;xUnit2013;</NoWarn>
</PropertyGroup>

View File

@@ -0,0 +1,20 @@
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: 'node16'
main: 'main.js'

View File

@@ -1 +1 @@
2.284.0
2.285.2