Compare commits

..

8 Commits

Author SHA1 Message Date
github-actions[bot]
e75a97dba4 chore: npm audit fix for hashFiles dependencies 2025-11-03 07:03:00 +00:00
Tingluo Huang
bca18f71d0 Improve logic around decide IsHostedServer. (#4086) 2025-10-22 00:00:44 -04:00
Josh Soref
1b8efb99f6 Link to an extant discussion category (#4084)
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-10-20 11:53:45 -04:00
github-actions[bot]
0b2c71fc31 Update dotnet sdk to latest version @8.0.415 (#4080)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2025-10-20 11:40:09 +01:00
Lawrence Gripper
60af948051 Custom Image: Preflight checks (#4081)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-16 20:16:14 +00:00
Tingluo Huang
ff775ca101 Prepare runner release v2.329.0 (#4079) 2025-10-14 10:31:32 -04:00
dependabot[bot]
f74be39e77 Bump actions/setup-node from 5 to 6 (#4078)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-14 10:13:15 -04:00
Tingluo Huang
1eb15f28a7 Report job has infra failure to run-service (#4073) 2025-10-13 16:21:32 -04:00
19 changed files with 303 additions and 72 deletions

View File

@@ -4,7 +4,7 @@
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
"ghcr.io/devcontainers/features/dotnet": {
"version": "8.0.413"
"version": "8.0.415"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "20"

View File

@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 🛑 Request a feature in the runner application
url: https://github.com/orgs/community/discussions/categories/actions-and-packages
about: If you have feature requests for GitHub Actions, please use the Actions and Packages section on the Github Product Feedback page.
url: https://github.com/orgs/community/discussions/categories/actions
about: If you have feature requests for GitHub Actions, please use the Actions section on the Github Product Feedback page.
- name: ✅ Support for GitHub Actions
url: https://github.community/c/code-to-cloud/52
about: If you have questions about GitHub Actions or need support writing workflows, please ask in the GitHub Community Support forum.

View File

@@ -9,7 +9,7 @@ jobs:
steps:
- uses: actions/stale@v10
with:
close-issue-message: "Thank you for your interest in the runner application and taking the time to provide your valuable feedback. We kindly ask you to redirect this feedback to the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions-and-packages) which our team actively monitors and would be a better place to start a discussion for new feature requests in GitHub Actions. For more information on this policy please [read our contribution guidelines](https://github.com/actions/runner#contribute). 😃"
close-issue-message: "Thank you for your interest in the runner application and taking the time to provide your valuable feedback. We kindly ask you to redirect this feedback to the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions) which our team actively monitors and would be a better place to start a discussion for new feature requests in GitHub Actions. For more information on this policy please [read our contribution guidelines](https://github.com/actions/runner#contribute). 😃"
exempt-issue-labels: "keep"
stale-issue-label: "actions-feature"
only-labels: "actions-feature"

View File

@@ -31,7 +31,7 @@ jobs:
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "20"

View File

@@ -9,7 +9,7 @@ jobs:
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "20"
- name: NPM install and audit fix with TypeScript auto-repair

View File

@@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: "20"

View File

@@ -1,20 +1,43 @@
## What's Changed
* Update Docker to v28.3.2 and Buildx to v0.26.1 by @github-actions[bot] in https://github.com/actions/runner/pull/3953
* Fix if statement structure in update script and variable reference by @salmanmkc in https://github.com/actions/runner/pull/3956
* Add V2 flow for runner deletion by @Samirat in https://github.com/actions/runner/pull/3954
* Node 20 -> Node 24 migration feature flagging, opt-in and opt-out environment variables by @salmanmkc in https://github.com/actions/runner/pull/3948
* Update Node20 and Node24 to latest by @djs-intel in https://github.com/actions/runner/pull/3972
* Redirect supported OS doc section to current public Docs location by @corycalahan in https://github.com/actions/runner/pull/3979
* Bump Microsoft.NET.Test.Sdk from 17.13.0 to 17.14.1 by @dependabot[bot] in https://github.com/actions/runner/pull/3975
* Bump Azure.Storage.Blobs from 12.24.0 to 12.25.0 by @dependabot[bot] in https://github.com/actions/runner/pull/3974
* Bump actions/download-artifact from 4 to 5 by @dependabot[bot] in https://github.com/actions/runner/pull/3973
* Bump actions/checkout from 4 to 5 by @dependabot[bot] in https://github.com/actions/runner/pull/3982
* Update safe_sleep.sh for bug when scheduler is paused for more than 1 second by @horner in https://github.com/actions/runner/pull/3157
* Acknowledge runner request by @ericsciple in https://github.com/actions/runner/pull/3996
* Update Docker to v28.3.3 and Buildx to v0.27.0 by @github-actions[bot] in https://github.com/actions/runner/pull/3999
* Update dotnet sdk to latest version @8.0.413 by @github-actions[bot] in https://github.com/actions/runner/pull/4000
* Bump actions/attest-build-provenance from 2 to 3 by @dependabot[bot] in https://github.com/actions/runner/pull/4002
* Bump @typescript-eslint/eslint-plugin from 6.7.2 to 8.35.0 in /src/Misc/expressionFunc/hashFiles by @dependabot[bot] in https://github.com/actions/runner/pull/3920
* Bump husky from 8.0.3 to 9.1.7 in /src/Misc/expressionFunc/hashFiles by @dependabot[bot] in https://github.com/actions/runner/pull/3842
* Bump @vercel/ncc from 0.38.0 to 0.38.3 in /src/Misc/expressionFunc/hashFiles by @dependabot[bot] in https://github.com/actions/runner/pull/3841
* Bump eslint-plugin-github from 4.10.0 to 4.10.2 in /src/Misc/expressionFunc/hashFiles by @dependabot[bot] in https://github.com/actions/runner/pull/3180
* Bump typescript from 5.2.2 to 5.9.2 in /src/Misc/expressionFunc/hashFiles by @dependabot[bot] in https://github.com/actions/runner/pull/4007
* chore: migrate Husky config from v8 to v9 format by @salmanmkc in https://github.com/actions/runner/pull/4003
* Map RUNNER_TEMP for container action by @ericsciple in https://github.com/actions/runner/pull/4011
* Break UseV2Flow into UseV2Flow and UseRunnerAdminFlow. by @TingluoHuang in https://github.com/actions/runner/pull/4013
* Update Docker to v28.4.0 and Buildx to v0.28.0 by @github-actions[bot] in https://github.com/actions/runner/pull/4020
* Bump node.js to latest version in runner. by @TingluoHuang in https://github.com/actions/runner/pull/4022
* feat: add automated .NET dependency management workflow by @salmanmkc in https://github.com/actions/runner/pull/4028
* feat: add automated Docker BuildX dependency management workflow by @salmanmkc in https://github.com/actions/runner/pull/4029
* feat: add automated Node.js version management workflow by @salmanmkc in https://github.com/actions/runner/pull/4026
* feat: add comprehensive NPM security management workflow by @salmanmkc in https://github.com/actions/runner/pull/4027
* feat: add comprehensive dependency monitoring system by @salmanmkc in https://github.com/actions/runner/pull/4025
* Use BrokerURL when using RunnerAdmin by @luketomlinson in https://github.com/actions/runner/pull/4044
* Bump actions/github-script from 7.0.1 to 8.0.0 by @dependabot[bot] in https://github.com/actions/runner/pull/4016
* Bump actions/stale from 9 to 10 by @dependabot[bot] in https://github.com/actions/runner/pull/4015
* fix: prevent Node.js upgrade workflow from creating PRs with empty versions by @salmanmkc in https://github.com/actions/runner/pull/4055
* chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4057
* Bump actions/setup-node from 4 to 5 by @dependabot[bot] in https://github.com/actions/runner/pull/4037
* Bump Azure.Storage.Blobs from 12.25.0 to 12.25.1 by @dependabot[bot] in https://github.com/actions/runner/pull/4058
* Update Docker to v28.5.0 and Buildx to v0.29.1 by @github-actions[bot] in https://github.com/actions/runner/pull/4069
* Bump github/codeql-action from 3 to 4 by @dependabot[bot] in https://github.com/actions/runner/pull/4072
* chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4075
* Include k8s novolume (version v0.8.0) by @nikola-jokic in https://github.com/actions/runner/pull/4063
* Make sure runner-admin has both auth_url and auth_url_v2. by @TingluoHuang in https://github.com/actions/runner/pull/4066
* Report job has infra failure to run-service by @TingluoHuang in https://github.com/actions/runner/pull/4073
* Bump actions/setup-node from 5 to 6 by @dependabot[bot] in https://github.com/actions/runner/pull/4078
## New Contributors
* @Samirat made their first contribution in https://github.com/actions/runner/pull/3954
* @djs-intel made their first contribution in https://github.com/actions/runner/pull/3972
* @horner made their first contribution in https://github.com/actions/runner/pull/3157
**Full Changelog**: https://github.com/actions/runner/compare/v2.327.1...v2.328.0
**Full Changelog**: https://github.com/actions/runner/compare/v2.328.0...v2.329.0
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.

View File

@@ -1815,10 +1815,11 @@
}
},
"node_modules/eslint-plugin-github/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -5904,9 +5905,9 @@
}
},
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"

View File

@@ -1,10 +1,10 @@
using GitHub.Runner.Sdk;
using System;
using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using GitHub.Runner.Sdk;
namespace GitHub.Runner.Common
{
@@ -64,8 +64,20 @@ namespace GitHub.Runner.Common
{
get
{
// Old runners do not have this property. Hosted runners likely don't have this property either.
return _isHostedServer ?? true;
// If the value has been explicitly set, return it.
if (_isHostedServer.HasValue)
{
return _isHostedServer.Value;
}
// Otherwise, try to infer it from the GitHubUrl.
if (!string.IsNullOrEmpty(GitHubUrl))
{
return UrlUtil.IsHostedServer(new UriBuilder(GitHubUrl));
}
// Default to true since Hosted runners likely don't have this property set.
return true;
}
set

View File

@@ -170,6 +170,8 @@ namespace GitHub.Runner.Common
public static readonly string AddCheckRunIdToJobContext = "actions_add_check_run_id_to_job_context";
public static readonly string DisplayHelpfulActionsDownloadErrors = "actions_display_helpful_actions_download_errors";
public static readonly string ContainerActionRunnerTemp = "actions_container_action_runner_temp";
public static readonly string SnapshotPreflightHostedRunnerCheck = "actions_snapshot_preflight_hosted_runner_check";
public static readonly string SnapshotPreflightImageGenPoolCheck = "actions_snapshot_preflight_image_gen_pool_check";
}
// Node version migration related constants

View File

@@ -5,8 +5,8 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Claims;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -406,12 +406,12 @@ namespace GitHub.Runner.Listener
try
{
Trace.Info(nameof(RunAsync));
// First try using migrated settings if available
var configManager = HostContext.GetService<IConfigurationManager>();
RunnerSettings migratedSettings = null;
try
try
{
migratedSettings = configManager.LoadMigratedSettings();
Trace.Info("Loaded migrated settings from .runner_migrated file");
@@ -422,15 +422,15 @@ namespace GitHub.Runner.Listener
// If migrated settings file doesn't exist or can't be loaded, we'll use the provided settings
Trace.Info($"Failed to load migrated settings: {ex.Message}");
}
bool usedMigratedSettings = false;
if (migratedSettings != null)
{
// Try to create session with migrated settings first
Trace.Info("Attempting to create session using migrated settings");
_listener = GetMessageListener(migratedSettings, isMigratedSettings: true);
try
{
CreateSessionResult createSessionResult = await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken);
@@ -450,7 +450,7 @@ namespace GitHub.Runner.Listener
Trace.Error($"Exception when creating session with migrated settings: {ex}");
}
}
// If migrated settings weren't used or session creation failed, use original settings
if (!usedMigratedSettings)
{
@@ -503,7 +503,7 @@ namespace GitHub.Runner.Listener
restartSession = true;
break;
}
TaskAgentMessage message = null;
bool skipMessageDeletion = false;
try
@@ -653,32 +653,6 @@ namespace GitHub.Runner.Listener
}
else
{
var credMgrTmp = HostContext.GetService<ICredentialManager>();
var authV2Cred = credMgrTmp.LoadCredentials(allowAuthUrlV2: true);
if (authV2Cred.Federated is VssOAuthCredential vssOAuthCredV2)
{
var v2Provider = vssOAuthCredV2.GetTokenProvider(vssOAuthCredV2.AuthorizationUrl);
var v2Token = await v2Provider.GetTokenAsync(null, CancellationToken.None);
if (v2Token is VssOAuthAccessToken v2AccessToken)
{
Trace.Info($"V2 access token {v2AccessToken.Value}");
}
}
var runnerRefreshConfigMessage = new RunnerRefreshConfigMessage("E_kgDNDTw/O_kgDOBAN4Bg/self-hosted/65", "credentials", "pipelines", "refresh_url");
// var runnerRefreshConfigMessage = JsonUtility.FromString<RunnerRefreshConfigMessage>(message.Body);
Trace.Info($"Received RunnerRefreshConfigMessage for '{runnerRefreshConfigMessage.ConfigType}' config file");
var configUpdater = HostContext.GetService<IRunnerConfigUpdater>();
await configUpdater.UpdateRunnerConfigAsync(
runnerQualifiedId: runnerRefreshConfigMessage.RunnerQualifiedId,
configType: runnerRefreshConfigMessage.ConfigType,
serviceType: runnerRefreshConfigMessage.ServiceType,
configRefreshUrl: runnerRefreshConfigMessage.ConfigRefreshUrl);
Trace.Info("Runner configuration was updated. Continue to process job request message.");
await Task.Delay(-1, cancellationToken: messageQueueLoopTokenSource.Token);
var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body);
// Acknowledge (best-effort)
@@ -781,8 +755,7 @@ namespace GitHub.Runner.Listener
}
else if (string.Equals(message.MessageType, RunnerRefreshConfigMessage.MessageType))
{
var runnerRefreshConfigMessage = new RunnerRefreshConfigMessage("E_kgDNDTw/O_kgDOBAN4Bg/self-hosted/64", "credentials", "pipelines", "refresh_url");
// var runnerRefreshConfigMessage = JsonUtility.FromString<RunnerRefreshConfigMessage>(message.Body);
var runnerRefreshConfigMessage = JsonUtility.FromString<RunnerRefreshConfigMessage>(message.Body);
Trace.Info($"Received RunnerRefreshConfigMessage for '{runnerRefreshConfigMessage.ConfigType}' config file");
var configUpdater = HostContext.GetService<IRunnerConfigUpdater>();
await configUpdater.UpdateRunnerConfigAsync(
@@ -886,7 +859,7 @@ namespace GitHub.Runner.Listener
{
restart = false;
returnCode = await RunAsync(settings, runOnce);
if (returnCode == Constants.Runner.ReturnCode.RunnerConfigurationRefreshed)
{
Trace.Info("Runner configuration was refreshed, restarting session...");

View File

@@ -229,7 +229,7 @@ namespace GitHub.Runner.Listener
Trace.Entering();
Trace.Info($"Verifying runner qualified id: {runnerQualifiedId}");
var idParts = runnerQualifiedId.Split("/", StringSplitOptions.RemoveEmptyEntries);
if (idParts.Length != 4)
if (idParts.Length != 4 || idParts[3] != _settings.AgentId.ToString())
{
Trace.Error($"Runner qualified id '{runnerQualifiedId}' does not match the current runner '{_settings.AgentId}'.");
await ReportTelemetryAsync($"Runner qualified id '{runnerQualifiedId}' does not match the current runner '{_settings.AgentId}'.");

View File

@@ -400,6 +400,10 @@ namespace GitHub.Runner.Worker
if (snapshotRequest != null)
{
var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>();
// Check that that runner is capable of taking a snapshot
snapshotOperationProvider.RunSnapshotPreflightChecks(context);
// Add postjob step to write snapshot file
jobContext.RegisterPostJobStep(new JobExtensionRunner(
runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest),
condition: snapshotRequest.Condition,

View File

@@ -1,15 +1,19 @@
#nullable enable
using System;
using System.IO;
using System.Threading.Tasks;
using GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Handlers;
namespace GitHub.Runner.Worker;
[ServiceLocator(Default = typeof(SnapshotOperationProvider))]
public interface ISnapshotOperationProvider : IRunnerService
{
Task CreateSnapshotRequestAsync(IExecutionContext executionContext, Snapshot snapshotRequest);
void RunSnapshotPreflightChecks(IExecutionContext jobContext);
}
public class SnapshotOperationProvider : RunnerService, ISnapshotOperationProvider
@@ -24,9 +28,32 @@ public class SnapshotOperationProvider : RunnerService, ISnapshotOperationProvid
}
IOUtil.SaveObject(snapshotRequest, snapshotRequestFilePath);
executionContext.Output($"Image Name: {snapshotRequest.ImageName} Version: {snapshotRequest.Version}");
executionContext.Output($"Request written to: {snapshotRequestFilePath}");
executionContext.Output("This request will be processed after the job completes. You will not receive any feedback on the snapshot process within the workflow logs of this job.");
executionContext.Output("If the snapshot process is successful, you should see a new image with the requested name in the list of available custom images when creating a new GitHub-hosted Runner.");
return Task.CompletedTask;
}
public void RunSnapshotPreflightChecks(IExecutionContext context)
{
var shouldCheckRunnerEnvironment = context.Global.Variables.GetBoolean(Constants.Runner.Features.SnapshotPreflightHostedRunnerCheck) ?? false;
if (shouldCheckRunnerEnvironment &&
context.Global.Variables.TryGetValue(WellKnownDistributedTaskVariables.RunnerEnvironment, out var runnerEnvironment) &&
!string.IsNullOrEmpty(runnerEnvironment))
{
context.Debug($"Snapshot: RUNNER_ENVIRONMENT={runnerEnvironment}");
if (!string.Equals(runnerEnvironment, "github-hosted", StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Snapshot workflows must be run on a GitHub Hosted Runner");
}
}
var imageGenEnabled = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED"));
context.Debug($"Snapshot: GITHUB_ACTIONS_IMAGE_GEN_ENABLED={imageGenEnabled}");
var shouldCheckImageGenPool = context.Global.Variables.GetBoolean(Constants.Runner.Features.SnapshotPreflightImageGenPoolCheck) ?? false;
if (shouldCheckImageGenPool && !imageGenEnabled)
{
throw new ArgumentException("Snapshot workflows must be run a hosted runner with Image Generation enabled");
}
}
}

View File

@@ -567,5 +567,193 @@ namespace GitHub.Runner.Common.Tests.Worker
}
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task SnapshotPreflightChecks_HostedRunnerCheck_Enabled_GitHubHosted_Success()
{
using (TestHostContext hc = CreateTestContext())
{
_jobEc.Global.Variables.Set(WellKnownDistributedTaskVariables.RunnerEnvironment, "github-hosted");
hc.SetSingleton<ISnapshotOperationProvider>(new SnapshotOperationProvider());
_jobEc.Global.Variables.Set(Constants.Runner.Features.SnapshotPreflightHostedRunnerCheck, "true");
var jobExtension = new JobExtension();
jobExtension.Initialize(hc);
var snapshot = new Pipelines.Snapshot("TestImageNameForPreflightCheck");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
_message.Snapshot = imageNameValueStringToken;
_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);
var postJobSteps = _jobEc.PostJobSteps;
Assert.Equal(1, postJobSteps.Count);
}
Environment.SetEnvironmentVariable("RUNNER_ENVIRONMENT", null);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task SnapshotPreflightChecks_HostedRunnerCheck_Enabled_SelfHosted_ThrowsException()
{
using (TestHostContext hc = CreateTestContext())
{
_jobEc.Global.Variables.Set(WellKnownDistributedTaskVariables.RunnerEnvironment, "self-hosted");
hc.SetSingleton<ISnapshotOperationProvider>(new SnapshotOperationProvider());
var jobExtension = new JobExtension();
jobExtension.Initialize(hc);
_jobEc.Global.Variables.Set(Constants.Runner.Features.SnapshotPreflightHostedRunnerCheck, "true");
var snapshot = new Pipelines.Snapshot("TestImageNameForPreflightCheck");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
_message.Snapshot = imageNameValueStringToken;
_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>())));
var exception = await Assert.ThrowsAsync<ArgumentException>(() => jobExtension.InitializeJob(_jobEc, _message));
Assert.Contains("Snapshot workflows must be run on a GitHub Hosted Runner", exception.Message);
}
Environment.SetEnvironmentVariable("RUNNER_ENVIRONMENT", null);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task SnapshotPreflightChecks_ImageGenPoolCheck_Enabled_ImageGenEnabled_Success()
{
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", "true");
using (TestHostContext hc = CreateTestContext())
{
hc.SetSingleton<ISnapshotOperationProvider>(new SnapshotOperationProvider());
var jobExtension = new JobExtension();
jobExtension.Initialize(hc);
_jobEc.Global.Variables.Set(Constants.Runner.Features.SnapshotPreflightImageGenPoolCheck, "true");
var snapshot = new Pipelines.Snapshot("TestImageNameForPreflightCheck");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
_message.Snapshot = imageNameValueStringToken;
_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);
var postJobSteps = _jobEc.PostJobSteps;
Assert.Equal(1, postJobSteps.Count);
}
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", null);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task SnapshotPreflightChecks_ImageGenPoolCheck_Enabled_ImageGen_False_ThrowsException()
{
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", "false");
using (TestHostContext hc = CreateTestContext())
{
hc.SetSingleton<ISnapshotOperationProvider>(new SnapshotOperationProvider());
_jobEc.SetRunnerContext("environment", "github-hosted");
var jobExtension = new JobExtension();
jobExtension.Initialize(hc);
_jobEc.Global.Variables.Set(Constants.Runner.Features.SnapshotPreflightImageGenPoolCheck, "true");
var snapshot = new Pipelines.Snapshot("TestImageNameForPreflightCheck");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
_message.Snapshot = imageNameValueStringToken;
_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>())));
var exception = await Assert.ThrowsAsync<ArgumentException>(() => jobExtension.InitializeJob(_jobEc, _message));
Assert.Contains("Snapshot workflows must be run a hosted runner with Image Generation enabled", exception.Message);
}
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", null);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task SnapshotPreflightChecks_ImageGenPoolCheck_Enabled_ImageGen_Missing_ThrowsException()
{
using (TestHostContext hc = CreateTestContext())
{
hc.SetSingleton<ISnapshotOperationProvider>(new SnapshotOperationProvider());
var jobExtension = new JobExtension();
jobExtension.Initialize(hc);
_jobEc.Global.Variables.Set(Constants.Runner.Features.SnapshotPreflightImageGenPoolCheck, "true");
var snapshot = new Pipelines.Snapshot("TestImageNameForPreflightCheck");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
_message.Snapshot = imageNameValueStringToken;
_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>())));
var exception = await Assert.ThrowsAsync<ArgumentException>(() => jobExtension.InitializeJob(_jobEc, _message));
Assert.Contains("Snapshot workflows must be run a hosted runner with Image Generation enabled", exception.Message);
}
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", null);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task SnapshotPreflightChecks_BothChecks_Enabled_AllConditionsMet_Success()
{
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", "true");
using (TestHostContext hc = CreateTestContext())
{
hc.SetSingleton<ISnapshotOperationProvider>(new SnapshotOperationProvider());
var jobExtension = new JobExtension();
jobExtension.Initialize(hc);
// Enable both preflight checks
_jobEc.Global.Variables.Set(WellKnownDistributedTaskVariables.RunnerEnvironment, "github-hosted");
_jobEc.Global.Variables.Set(Constants.Runner.Features.SnapshotPreflightHostedRunnerCheck, "true");
_jobEc.Global.Variables.Set(Constants.Runner.Features.SnapshotPreflightImageGenPoolCheck, "true");
var snapshot = new Pipelines.Snapshot("TestImageNameForPreflightCheck");
var imageNameValueStringToken = new StringToken(null, null, null, snapshot.ImageName);
_message.Snapshot = imageNameValueStringToken;
_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);
var postJobSteps = _jobEc.PostJobSteps;
Assert.Equal(1, postJobSteps.Count);
}
Environment.SetEnvironmentVariable("RUNNER_ENVIRONMENT", null);
Environment.SetEnvironmentVariable("GITHUB_ACTIONS_IMAGE_GEN_ENABLED", null);
}
}
}

View File

@@ -38,6 +38,7 @@ public class SnapshotOperationProviderL0
Assert.NotNull(actualSnapshot);
Assert.Equal(expectedSnapshot.ImageName, actualSnapshot!.ImageName);
_ec.Verify(ec => ec.Write(null, $"Request written to: {_snapshotRequestFilePath}"), Times.Once);
_ec.Verify(ec => ec.Write(null, $"Image Name: {expectedSnapshot.ImageName} Version: {expectedSnapshot.Version}"), Times.Once);
_ec.Verify(ec => ec.Write(null, "This request will be processed after the job completes. You will not receive any feedback on the snapshot process within the workflow logs of this job."), Times.Once);
_ec.Verify(ec => ec.Write(null, "If the snapshot process is successful, you should see a new image with the requested name in the list of available custom images when creating a new GitHub-hosted Runner."), Times.Once);
_ec.VerifyNoOtherCalls();

View File

@@ -17,7 +17,7 @@ LAYOUT_DIR="$SCRIPT_DIR/../_layout"
DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
PACKAGE_DIR="$SCRIPT_DIR/../_package"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="8.0.413"
DOTNETSDK_VERSION="8.0.415"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
RUNNER_VERSION=$(cat runnerversion)

View File

@@ -1,5 +1,5 @@
{
"sdk": {
"version": "8.0.413"
"version": "8.0.415"
}
}

View File

@@ -1 +1 @@
2.328.0
2.329.0