mirror of
https://github.com/actions/runner.git
synced 2026-07-05 03:54:40 +08:00
Compare commits
1 Commits
philip-gai
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90afc4e0a4 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Build runner layout
|
||||
- name: Build & Layout Release
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
docker_platform: linux/arm64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Get latest runner version
|
||||
id: latest_runner
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
2
.github/workflows/dependency-check.yml
vendored
2
.github/workflows/dependency-check.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
npm-vulnerabilities: ${{ steps.check-versions.outputs.npm-vulnerabilities }}
|
||||
open-dependency-prs: ${{ steps.check-prs.outputs.open-dependency-prs }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
4
.github/workflows/docker-buildx-upgrade.yml
vendored
4
.github/workflows/docker-buildx-upgrade.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
BUILDX_CURRENT_VERSION: ${{ steps.check_buildx_version.outputs.CURRENT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check Docker version
|
||||
id: check_docker_version
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Update Docker version
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.inputs.releaseBranch }}
|
||||
|
||||
|
||||
4
.github/workflows/dotnet-upgrade.yml
vendored
4
.github/workflows/dotnet-upgrade.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
DOTNET_CURRENT_MAJOR_MINOR_VERSION: ${{ steps.fetch_current_version.outputs.DOTNET_CURRENT_MAJOR_MINOR_VERSION }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
- name: Get current major minor version
|
||||
id: fetch_current_version
|
||||
shell: bash
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
if: ${{ needs.dotnet-update.outputs.SHOULD_UPDATE == 1 && needs.dotnet-update.outputs.BRANCH_EXISTS == 0 }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: feature/dotnetsdk-upgrade/${{ needs.dotnet-update.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}
|
||||
- name: Create Pull Request
|
||||
|
||||
2
.github/workflows/node-upgrade.yml
vendored
2
.github/workflows/node-upgrade.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
update-node:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- name: Get latest Node versions
|
||||
id: node-versions
|
||||
run: |
|
||||
|
||||
2
.github/workflows/npm-audit-typescript.yml
vendored
2
.github/workflows/npm-audit-typescript.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
npm-audit-with-ts-fix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
2
.github/workflows/npm-audit.yml
vendored
2
.github/workflows/npm-audit.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
npm-audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Make sure ./releaseVersion match ./src/runnerversion
|
||||
# Query GitHub release ensure version is not used
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Build runner layout
|
||||
- name: Build & Layout Release
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Download runner package tar.gz/zip produced by 'build' job
|
||||
- name: Download Artifact (win-x64)
|
||||
@@ -296,7 +296,7 @@ jobs:
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Compute image version
|
||||
id: image
|
||||
|
||||
@@ -5,8 +5,8 @@ ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG RUNNER_VERSION
|
||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.7.0
|
||||
ARG DOCKER_VERSION=29.6.0
|
||||
ARG BUILDX_VERSION=0.35.0
|
||||
ARG DOCKER_VERSION=29.5.3
|
||||
ARG BUILDX_VERSION=0.34.1
|
||||
|
||||
RUN apt update -y && apt install curl unzip -y
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
|
||||
# When you update Node versions you must also create a new release of alpine_nodejs at that updated version.
|
||||
# Follow the instructions here: https://github.com/actions/alpine_nodejs?tab=readme-ov-file#getting-started
|
||||
NODE20_VERSION="20.20.2"
|
||||
NODE24_VERSION="24.18.0"
|
||||
NODE24_VERSION="24.16.0"
|
||||
|
||||
get_abs_path() {
|
||||
# exploits the fact that pwd will print abs path when no args
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.3" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="System.Threading.Channels" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -228,30 +228,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
throw new Exception($"Missing download info for {lookupKey}");
|
||||
}
|
||||
|
||||
Exception downloadFailure = null;
|
||||
try
|
||||
{
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
downloadFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "download_action",
|
||||
Result = downloadFailure == null ? "succeeded" : downloadFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
}
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
}
|
||||
|
||||
// Parse action.yml and collect composite sub-actions for batched
|
||||
@@ -421,30 +398,7 @@ namespace GitHub.Runner.Worker
|
||||
if (repositoryActions.Count > 0)
|
||||
{
|
||||
// Get the download info
|
||||
IDictionary<string, WebApi.ActionDownloadInfo> downloadInfos = null;
|
||||
Exception resolveFailure = null;
|
||||
try
|
||||
{
|
||||
downloadInfos = await GetDownloadInfoAsync(executionContext, repositoryActions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
resolveFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "resolve_actions",
|
||||
Result = resolveFailure == null ? "succeeded" : resolveFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
}
|
||||
var downloadInfos = await GetDownloadInfoAsync(executionContext, repositoryActions);
|
||||
|
||||
// Download each action
|
||||
foreach (var action in repositoryActions)
|
||||
@@ -460,29 +414,7 @@ namespace GitHub.Runner.Worker
|
||||
throw new Exception($"Missing download info for {lookupKey}");
|
||||
}
|
||||
|
||||
Exception downloadFailure = null;
|
||||
try
|
||||
{
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
downloadFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "download_action",
|
||||
Result = downloadFailure == null ? "succeeded" : downloadFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
}
|
||||
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
|
||||
}
|
||||
|
||||
// More preparation based on content in the repository (action.yml)
|
||||
@@ -1048,33 +980,10 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
if (actionsToResolve.Count > 0)
|
||||
{
|
||||
IDictionary<string, WebApi.ActionDownloadInfo> downloadInfos = null;
|
||||
Exception resolveFailure = null;
|
||||
try
|
||||
var downloadInfos = await GetDownloadInfoAsync(executionContext, actionsToResolve);
|
||||
foreach (var kvp in downloadInfos)
|
||||
{
|
||||
downloadInfos = await GetDownloadInfoAsync(executionContext, actionsToResolve);
|
||||
foreach (var kvp in downloadInfos)
|
||||
{
|
||||
resolvedDownloadInfos[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// record the exception for telemetry, and rethrow the original exception to fail the step.
|
||||
resolveFailure = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
|
||||
{
|
||||
Operation = "resolve_actions",
|
||||
Result = resolveFailure == null ? "succeeded" : resolveFailure.GetType().Name
|
||||
}, Newtonsoft.Json.Formatting.None)}"
|
||||
});
|
||||
resolvedDownloadInfos[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1199,6 +1108,12 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache}"
|
||||
});
|
||||
|
||||
if (!useActionArchiveCache)
|
||||
{
|
||||
await DownloadRepositoryArchive(executionContext, link, downloadInfo.Authentication?.Token, archiveFile);
|
||||
@@ -1207,13 +1122,6 @@ namespace GitHub.Runner.Worker
|
||||
var stagingDirectory = Path.Combine(tempDirectory, "_staging");
|
||||
Directory.CreateDirectory(stagingDirectory);
|
||||
|
||||
var fileInfo = new FileInfo(archiveFile);
|
||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||
{
|
||||
Type = JobTelemetryType.General,
|
||||
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache} size {fileInfo.Length} bytes"
|
||||
});
|
||||
|
||||
#if OS_WINDOWS
|
||||
try
|
||||
{
|
||||
@@ -1251,6 +1159,7 @@ namespace GitHub.Runner.Worker
|
||||
int exitCode = await processInvoker.ExecuteAsync(stagingDirectory, tar, $"-xzf \"{archiveFile}\"", null, executionContext.CancellationToken);
|
||||
if (exitCode != 0)
|
||||
{
|
||||
var fileInfo = new FileInfo(archiveFile);
|
||||
var sha256hash = await IOUtil.GetFileContentSha256HashAsync(archiveFile);
|
||||
throw new InvalidActionArchiveException($"Can't use 'tar -xzf' extract archive file: {archiveFile} (SHA256 '{sha256hash}', size '{fileInfo.Length}' bytes, tar outputs '{string.Join(' ', tarOutputs)}'). Action being checked out: {downloadInfo.NameWithOwner}@{downloadInfo.Ref}. return code: {exitCode}.");
|
||||
}
|
||||
@@ -1300,12 +1209,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private string GetWatermarkFilePath(string directory) => directory + ".completed";
|
||||
|
||||
private sealed class ActionTelemetryPayload
|
||||
{
|
||||
public string Operation { get; set; }
|
||||
public string Result { get; set; }
|
||||
}
|
||||
|
||||
private ActionSetupInfo PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction)
|
||||
{
|
||||
var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference;
|
||||
|
||||
@@ -239,11 +239,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
Environment["ACTIONS_RESULTS_URL"] = resultsUrl;
|
||||
}
|
||||
|
||||
if (ExecutionContext.Global.Variables.TryGetValue("actions_cache_mode", out var cacheMode) && !string.IsNullOrEmpty(cacheMode))
|
||||
{
|
||||
Environment["ACTIONS_CACHE_MODE"] = cacheMode;
|
||||
}
|
||||
|
||||
if (ExecutionContext.Global.Variables.GetBoolean(Constants.Runner.Features.SetOrchestrationIdEnvForActions) ?? false)
|
||||
{
|
||||
if (ExecutionContext.Global.Variables.TryGetValue(Constants.Variables.System.OrchestrationId, out var orchestrationId) && !string.IsNullOrEmpty(orchestrationId))
|
||||
|
||||
@@ -78,11 +78,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
Environment["ACTIONS_CACHE_SERVICE_V2"] = bool.TrueString;
|
||||
}
|
||||
|
||||
if (ExecutionContext.Global.Variables.TryGetValue("actions_cache_mode", out var cacheMode) && !string.IsNullOrEmpty(cacheMode))
|
||||
{
|
||||
Environment["ACTIONS_CACHE_MODE"] = cacheMode;
|
||||
}
|
||||
|
||||
if (ExecutionContext.Global.Variables.GetBoolean(Constants.Runner.Features.SetOrchestrationIdEnvForActions) ?? false)
|
||||
{
|
||||
if (ExecutionContext.Global.Variables.TryGetValue(Constants.Variables.System.OrchestrationId, out var orchestrationId) && !string.IsNullOrEmpty(orchestrationId))
|
||||
|
||||
@@ -171,12 +171,6 @@ namespace GitHub.Runner.Worker
|
||||
context.Output($"Secret source: {secretSource}");
|
||||
}
|
||||
|
||||
var cacheMode = jobContext.Global.Variables.Get("actions_cache_mode");
|
||||
if (!string.IsNullOrEmpty(cacheMode))
|
||||
{
|
||||
context.Output($"Actions cache-mode: {cacheMode}");
|
||||
}
|
||||
|
||||
var repoFullName = context.GetGitHubContext("repository");
|
||||
ArgUtil.NotNull(repoFullName, nameof(repoFullName));
|
||||
context.Debug($"Primary repository: {repoFullName}");
|
||||
|
||||
@@ -90,11 +90,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main", "action.yml");
|
||||
Assert.True(File.Exists(actionYamlFile));
|
||||
|
||||
var telemetryMessages = GetTelemetryMessages();
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "resolve_actions"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "succeeded"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "download_action"));
|
||||
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
||||
}
|
||||
finally
|
||||
@@ -153,11 +148,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
// Act + Assert
|
||||
await Assert.ThrowsAsync<InvalidActionArchiveException>(async () => await _actionManager.PrepareActionsAsync(_ec.Object, actions));
|
||||
|
||||
var telemetryMessages = GetTelemetryMessages();
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "resolve_actions"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "download_action"));
|
||||
Assert.True(ContainsTelemetry(telemetryMessages, "InvalidActionArchiveException"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -225,51 +215,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task PrepareActions_ResolveActionDownloadInfo_RecordsTelemetry_OnFailure()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Arrange
|
||||
Setup();
|
||||
_ec.Object.Global.Variables.Set(Constants.Variables.System.JobRequestType, "RunnerJobRequest");
|
||||
|
||||
_launchServer
|
||||
.Setup(x => x.ResolveActionsDownloadInfoAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<ActionReferenceList>(), It.IsAny<CancellationToken>(), It.IsAny<bool>()))
|
||||
.ThrowsAsync(new Exception("resolve failed"));
|
||||
|
||||
var actions = new List<Pipelines.JobStep>
|
||||
{
|
||||
new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action",
|
||||
Id = Guid.NewGuid(),
|
||||
Reference = new Pipelines.RepositoryPathReference()
|
||||
{
|
||||
Name = "actions/checkout",
|
||||
Ref = "v4",
|
||||
RepositoryType = "GitHub"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Act + Assert
|
||||
await Assert.ThrowsAsync<FailedToResolveActionDownloadInfoException>(async () => await _actionManager.PrepareActionsAsync(_ec.Object, actions));
|
||||
|
||||
var telemetryMessages = GetTelemetryMessages();
|
||||
Assert.Equal(1, telemetryMessages.Count(message =>
|
||||
message.Contains("resolve_actions", StringComparison.OrdinalIgnoreCase)
|
||||
&& !message.Contains("\"result\":\"succeeded\"", StringComparison.OrdinalIgnoreCase)));
|
||||
Assert.False(ContainsTelemetry(telemetryMessages, "resolve_actions\",\"result\":\"succeeded"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
#if OS_LINUX
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
@@ -3388,16 +3333,6 @@ runs:
|
||||
}
|
||||
}
|
||||
|
||||
private IList<string> GetTelemetryMessages()
|
||||
{
|
||||
return _ec.Object.Global.JobTelemetry.Select(x => x.Message).ToList();
|
||||
}
|
||||
|
||||
private static bool ContainsTelemetry(IList<string> telemetryMessages, string expectedFragment)
|
||||
{
|
||||
return telemetryMessages.Any(message => message.Contains(expectedFragment, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Actions.RunService.WebApi;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Container.ContainerHooks;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
@@ -94,260 +85,5 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Assert.Equal("ubuntu:20.04", _stepTelemetry.Action);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
[InlineData("read")]
|
||||
[InlineData("none")]
|
||||
[InlineData("write")]
|
||||
[InlineData("write-only")]
|
||||
public async Task RunAsync_ExportsCacheModeEnv_WhenVariableSet(string mode)
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var environment = await RunNodeScriptActionHandlerAsync(hc, new Dictionary<string, VariableValue>
|
||||
{
|
||||
{ "actions_cache_mode", mode }
|
||||
});
|
||||
|
||||
Assert.True(environment.TryGetValue("ACTIONS_CACHE_MODE", out var value));
|
||||
Assert.Equal(mode, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task RunAsync_DoesNotExportCacheModeEnv_WhenVariableAbsent()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var environment = await RunNodeScriptActionHandlerAsync(hc, new Dictionary<string, VariableValue>());
|
||||
|
||||
Assert.False(environment.ContainsKey("ACTIONS_CACHE_MODE"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task RunAsync_DoesNotExportCacheModeEnv_WhenVariableEmpty()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var environment = await RunNodeScriptActionHandlerAsync(hc, new Dictionary<string, VariableValue>
|
||||
{
|
||||
{ "actions_cache_mode", "" }
|
||||
});
|
||||
|
||||
Assert.False(environment.ContainsKey("ACTIONS_CACHE_MODE"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task RunAsync_CacheModeCoexistsWithCacheServiceV2()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var environment = await RunNodeScriptActionHandlerAsync(hc, new Dictionary<string, VariableValue>
|
||||
{
|
||||
{ "actions_uses_cache_service_v2", "true" },
|
||||
{ "actions_cache_mode", "read" }
|
||||
});
|
||||
|
||||
Assert.Equal(bool.TrueString, environment["ACTIONS_CACHE_SERVICE_V2"]);
|
||||
Assert.Equal("read", environment["ACTIONS_CACHE_MODE"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task RunAsync_DoesNotAffectRuntimeEnv_WhenCacheModeAbsent()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var environment = await RunNodeScriptActionHandlerAsync(hc, new Dictionary<string, VariableValue>());
|
||||
|
||||
// Baseline runtime env is still exported and cache-mode adds nothing.
|
||||
Assert.Equal("https://pipelines.actions.githubusercontent.com/", environment["ACTIONS_RUNTIME_URL"]);
|
||||
Assert.Equal("token", environment["ACTIONS_RUNTIME_TOKEN"]);
|
||||
Assert.False(environment.ContainsKey("ACTIONS_CACHE_MODE"));
|
||||
Assert.False(environment.ContainsKey("ACTIONS_CACHE_SERVICE_V2"));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
[InlineData("read")]
|
||||
[InlineData("none")]
|
||||
public async Task ContainerRunAsync_ExportsCacheModeEnv_WhenVariableSet(string mode)
|
||||
{
|
||||
// Container actions only run on Linux; RunAsync throws on other platforms.
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var container = await RunContainerActionHandlerAsync(hc, new Dictionary<string, VariableValue>
|
||||
{
|
||||
{ "actions_cache_mode", mode }
|
||||
});
|
||||
|
||||
Assert.True(container.ContainerEnvironmentVariables.TryGetValue("ACTIONS_CACHE_MODE", out var value));
|
||||
Assert.Equal(mode, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task ContainerRunAsync_DoesNotExportCacheModeEnv_WhenVariableAbsent()
|
||||
{
|
||||
// Container actions only run on Linux; RunAsync throws on other platforms.
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var container = await RunContainerActionHandlerAsync(hc, new Dictionary<string, VariableValue>());
|
||||
|
||||
Assert.False(container.ContainerEnvironmentVariables.ContainsKey("ACTIONS_CACHE_MODE"));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ContainerInfo> RunContainerActionHandlerAsync(TestHostContext hc, IDictionary<string, VariableValue> variables)
|
||||
{
|
||||
// Route through the container-hooks path so the handler skips docker build/run.
|
||||
variables[Constants.Runner.Features.AllowRunnerContainerHooks] = "true";
|
||||
Environment.SetEnvironmentVariable(Constants.Hooks.ContainerHooksPath, Path.Combine(hc.GetDirectory(WellKnownDirectory.Root), "hooks.js"));
|
||||
|
||||
var tempDirectory = hc.GetDirectory(WellKnownDirectory.Temp);
|
||||
Directory.CreateDirectory(Path.Combine(tempDirectory, "_runner_file_commands"));
|
||||
Directory.CreateDirectory(Path.Combine(tempDirectory, "_github_workflow"));
|
||||
var workspace = Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), "workspace");
|
||||
Directory.CreateDirectory(workspace);
|
||||
|
||||
var serverVariables = new Variables(hc, variables);
|
||||
var endpoints = new List<ServiceEndpoint>
|
||||
{
|
||||
new ServiceEndpoint()
|
||||
{
|
||||
Name = WellKnownServiceEndpointNames.SystemVssConnection,
|
||||
Url = new Uri("https://pipelines.actions.githubusercontent.com"),
|
||||
Authorization = new EndpointAuthorization()
|
||||
{
|
||||
Scheme = "Test",
|
||||
Parameters = { { "AccessToken", "token" } }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_ec.Setup(x => x.Global).Returns(new GlobalContext()
|
||||
{
|
||||
Variables = serverVariables,
|
||||
Endpoints = endpoints,
|
||||
PrependPath = new List<string>(),
|
||||
EnvironmentVariables = new Dictionary<string, string>()
|
||||
});
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.JobContext).Returns(new JobContext());
|
||||
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(workspace);
|
||||
|
||||
ContainerInfo captured = null;
|
||||
var hookManager = new Mock<IContainerHookManager>();
|
||||
hookManager.Setup(x => x.RunContainerStepAsync(It.IsAny<IExecutionContext>(), It.IsAny<ContainerInfo>(), It.IsAny<string>()))
|
||||
.Callback((IExecutionContext ec, ContainerInfo container, string dockerFile) => { captured = container; })
|
||||
.Returns(Task.CompletedTask);
|
||||
hc.SetSingleton(hookManager.Object);
|
||||
hc.SetSingleton(new Mock<IActionManifestManagerWrapper>().Object);
|
||||
|
||||
var handler = new ContainerActionHandler();
|
||||
handler.Initialize(hc);
|
||||
handler.ExecutionContext = _ec.Object;
|
||||
handler.Environment = new Dictionary<string, string>();
|
||||
handler.Inputs = new Dictionary<string, string>();
|
||||
handler.Action = new ContainerRegistryReference() { Image = "alpine:latest" };
|
||||
handler.Data = new ContainerActionExecutionData() { Image = "docker://alpine:latest" };
|
||||
|
||||
await handler.RunAsync(ActionRunStage.Main);
|
||||
|
||||
return captured;
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, string>> RunNodeScriptActionHandlerAsync(TestHostContext hc, IDictionary<string, VariableValue> variables)
|
||||
{
|
||||
var actionDirectory = Path.Combine(hc.GetDirectory(WellKnownDirectory.Work), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(actionDirectory);
|
||||
var scriptFile = "main.js";
|
||||
File.WriteAllText(Path.Combine(actionDirectory, scriptFile), "// noop");
|
||||
|
||||
var serverVariables = new Variables(hc, variables);
|
||||
var endpoints = new List<ServiceEndpoint>
|
||||
{
|
||||
new ServiceEndpoint()
|
||||
{
|
||||
Name = WellKnownServiceEndpointNames.SystemVssConnection,
|
||||
Url = new Uri("https://pipelines.actions.githubusercontent.com"),
|
||||
Authorization = new EndpointAuthorization()
|
||||
{
|
||||
Scheme = "Test",
|
||||
Parameters = { { "AccessToken", "token" } }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_ec.Setup(x => x.Global).Returns(new GlobalContext()
|
||||
{
|
||||
Variables = serverVariables,
|
||||
Endpoints = endpoints,
|
||||
PrependPath = new List<string>(),
|
||||
EnvironmentVariables = new Dictionary<string, string>()
|
||||
});
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(actionDirectory);
|
||||
_ec.Setup(x => x.GetMatchers()).Returns(new List<IssueMatcherConfig>());
|
||||
_ec.Setup(x => x.ForceCompleted).Returns(new TaskCompletionSource<int>().Task);
|
||||
_ec.Setup(x => x.CancellationToken).Returns(CancellationToken.None);
|
||||
|
||||
var stepHost = new Mock<IStepHost>();
|
||||
stepHost.Setup(x => x.DetermineNodeRuntimeVersion(It.IsAny<IExecutionContext>(), It.IsAny<string>())).ReturnsAsync("node20");
|
||||
stepHost.Setup(x => x.ResolvePathForStepHost(It.IsAny<IExecutionContext>(), It.IsAny<string>())).Returns((IExecutionContext ec, string path) => path);
|
||||
stepHost.Setup(x => x.ExecuteAsync(
|
||||
It.IsAny<IExecutionContext>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<IDictionary<string, string>>(),
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<System.Text.Encoding>(),
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<CancellationToken>())).ReturnsAsync(0);
|
||||
|
||||
var handler = new NodeScriptActionHandler();
|
||||
handler.Initialize(hc);
|
||||
handler.ExecutionContext = _ec.Object;
|
||||
handler.StepHost = stepHost.Object;
|
||||
handler.Environment = new Dictionary<string, string>();
|
||||
handler.Inputs = new Dictionary<string, string>();
|
||||
handler.RuntimeVariables = serverVariables;
|
||||
handler.ActionDirectory = actionDirectory;
|
||||
handler.Action = new RepositoryPathReference() { Name = "actions/checkout", Ref = "v2" };
|
||||
handler.Data = new NodeJSActionExecutionData() { Script = scriptFile, NodeVersion = "node20" };
|
||||
|
||||
await handler.RunAsync(ActionRunStage.Main);
|
||||
|
||||
return handler.Environment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,54 +238,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
[InlineData("read")]
|
||||
[InlineData("none")]
|
||||
[InlineData("write")]
|
||||
[InlineData("write-only")]
|
||||
public async Task InitializeJob_LogsCacheMode_WhenVariableSet(string mode)
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
_jobEc.Global.Variables.Set("actions_cache_mode", mode);
|
||||
|
||||
var jobExtension = new JobExtension();
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>(), It.IsAny<Guid>()))
|
||||
.Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>(), new Dictionary<Guid, IActionRunner>())));
|
||||
|
||||
await jobExtension.InitializeJob(_jobEc, _message);
|
||||
|
||||
_jobServerQueue.Verify(
|
||||
x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.Is<string>(m => m.Contains($"Actions cache-mode: {mode}")), It.IsAny<long?>()),
|
||||
Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task InitializeJob_DoesNotLogCacheMode_WhenVariableAbsent()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var jobExtension = new JobExtension();
|
||||
jobExtension.Initialize(hc);
|
||||
|
||||
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>(), It.IsAny<Guid>()))
|
||||
.Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>(), new Dictionary<Guid, IActionRunner>())));
|
||||
|
||||
await jobExtension.InitializeJob(_jobEc, _message);
|
||||
|
||||
_jobServerQueue.Verify(
|
||||
x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.Is<string>(m => m.Contains("Actions cache-mode:")), It.IsAny<long?>()),
|
||||
Times.Never);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
|
||||
Reference in New Issue
Block a user