mirror of
https://github.com/actions/runner.git
synced 2025-12-12 05:37:01 +00:00
cache actions on premises (#381)
This commit is contained in:
@@ -15,6 +15,9 @@ namespace GitHub.Runner.Common
|
|||||||
[DataContract]
|
[DataContract]
|
||||||
public sealed class RunnerSettings
|
public sealed class RunnerSettings
|
||||||
{
|
{
|
||||||
|
[DataMember(Name = "IsHostedServer", EmitDefaultValue = false)]
|
||||||
|
private bool? _isHostedServer;
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public int AgentId { get; set; }
|
public int AgentId { get; set; }
|
||||||
|
|
||||||
@@ -42,6 +45,21 @@ namespace GitHub.Runner.Common
|
|||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string MonitorSocketAddress { get; set; }
|
public string MonitorSocketAddress { get; set; }
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public bool IsHostedServer
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Old runners do not have this property. Hosted runners likely don't have this property either.
|
||||||
|
return _isHostedServer ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isHostedServer = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
// Computed property for convenience. Can either return:
|
// Computed property for convenience. Can either return:
|
||||||
// 1. If runner was configured at the repo level, returns something like: "myorg/myrepo"
|
// 1. If runner was configured at the repo level, returns something like: "myorg/myrepo"
|
||||||
@@ -69,6 +87,15 @@ namespace GitHub.Runner.Common
|
|||||||
return repoOrOrgName;
|
return repoOrOrgName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[OnSerializing]
|
||||||
|
private void OnSerializing(StreamingContext context)
|
||||||
|
{
|
||||||
|
if (_isHostedServer.HasValue && _isHostedServer.Value)
|
||||||
|
{
|
||||||
|
_isHostedServer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ServiceLocator(Default = typeof(ConfigurationStore))]
|
[ServiceLocator(Default = typeof(ConfigurationStore))]
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
|
|
||||||
RunnerSettings runnerSettings = new RunnerSettings();
|
RunnerSettings runnerSettings = new RunnerSettings();
|
||||||
|
|
||||||
bool isHostedServer = false;
|
|
||||||
// Loop getting url and creds until you can connect
|
// Loop getting url and creds until you can connect
|
||||||
ICredentialProvider credProvider = null;
|
ICredentialProvider credProvider = null;
|
||||||
VssCredentials creds = null;
|
VssCredentials creds = null;
|
||||||
@@ -117,7 +116,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
||||||
isHostedServer = await IsHostedServer(runnerSettings.ServerUrl, creds);
|
runnerSettings.IsHostedServer = await IsHostedServer(runnerSettings.ServerUrl, creds);
|
||||||
|
|
||||||
// Validate can connect.
|
// Validate can connect.
|
||||||
await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), creds);
|
await _runnerServer.ConnectAsync(new Uri(runnerSettings.ServerUrl), creds);
|
||||||
@@ -248,7 +247,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
{
|
{
|
||||||
UriBuilder configServerUrl = new UriBuilder(runnerSettings.ServerUrl);
|
UriBuilder configServerUrl = new UriBuilder(runnerSettings.ServerUrl);
|
||||||
UriBuilder oauthEndpointUrlBuilder = new UriBuilder(agent.Authorization.AuthorizationUrl);
|
UriBuilder oauthEndpointUrlBuilder = new UriBuilder(agent.Authorization.AuthorizationUrl);
|
||||||
if (!isHostedServer && Uri.Compare(configServerUrl.Uri, oauthEndpointUrlBuilder.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0)
|
if (!runnerSettings.IsHostedServer && Uri.Compare(configServerUrl.Uri, oauthEndpointUrlBuilder.Uri, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0)
|
||||||
{
|
{
|
||||||
oauthEndpointUrlBuilder.Scheme = configServerUrl.Scheme;
|
oauthEndpointUrlBuilder.Scheme = configServerUrl.Scheme;
|
||||||
oauthEndpointUrlBuilder.Host = configServerUrl.Host;
|
oauthEndpointUrlBuilder.Host = configServerUrl.Host;
|
||||||
@@ -381,7 +380,6 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
|
||||||
bool isHostedServer = await IsHostedServer(settings.ServerUrl, creds);
|
|
||||||
await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
|
await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
|
||||||
|
|
||||||
var agents = await _runnerServer.GetAgentsAsync(settings.PoolId, settings.AgentName);
|
var agents = await _runnerServer.GetAgentsAsync(settings.PoolId, settings.AgentName);
|
||||||
|
|||||||
@@ -58,8 +58,14 @@ namespace GitHub.Runner.Worker
|
|||||||
executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is depreciated. Please remove it from the repository's secrets");
|
executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is depreciated. Please remove it from the repository's secrets");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the cache (local runner)
|
// Clear the cache (for self-hosted runners)
|
||||||
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
// Note, temporarily avoid this step for the on-premises product, to avoid rate limiting.
|
||||||
|
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||||
|
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||||
|
if (isHostedServer)
|
||||||
|
{
|
||||||
|
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
{
|
{
|
||||||
@@ -448,7 +454,8 @@ namespace GitHub.Runner.Worker
|
|||||||
ArgUtil.NotNullOrEmpty(repositoryReference.Ref, nameof(repositoryReference.Ref));
|
ArgUtil.NotNullOrEmpty(repositoryReference.Ref, nameof(repositoryReference.Ref));
|
||||||
|
|
||||||
string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref);
|
string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref);
|
||||||
if (File.Exists(destDirectory + ".completed"))
|
string watermarkFile = destDirectory + ".completed";
|
||||||
|
if (File.Exists(watermarkFile))
|
||||||
{
|
{
|
||||||
executionContext.Debug($"Action '{repositoryReference.Name}@{repositoryReference.Ref}' already downloaded at '{destDirectory}'.");
|
executionContext.Debug($"Action '{repositoryReference.Name}@{repositoryReference.Ref}' already downloaded at '{destDirectory}'.");
|
||||||
return;
|
return;
|
||||||
@@ -498,24 +505,33 @@ namespace GitHub.Runner.Worker
|
|||||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||||
using (var httpClient = new HttpClient(httpClientHandler))
|
using (var httpClient = new HttpClient(httpClientHandler))
|
||||||
{
|
{
|
||||||
var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN");
|
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||||
if (string.IsNullOrEmpty(authToken))
|
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||||
|
if (isHostedServer)
|
||||||
{
|
{
|
||||||
// TODO: Depreciate the PREVIEW_ACTION_TOKEN
|
var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN");
|
||||||
authToken = executionContext.Variables.Get("PREVIEW_ACTION_TOKEN");
|
if (string.IsNullOrEmpty(authToken))
|
||||||
}
|
{
|
||||||
|
// TODO: Depreciate the PREVIEW_ACTION_TOKEN
|
||||||
|
authToken = executionContext.Variables.Get("PREVIEW_ACTION_TOKEN");
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(authToken))
|
if (!string.IsNullOrEmpty(authToken))
|
||||||
{
|
{
|
||||||
HostContext.SecretMasker.AddValue(authToken);
|
HostContext.SecretMasker.AddValue(authToken);
|
||||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"PAT:{authToken}"));
|
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"PAT:{authToken}"));
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var accessToken = executionContext.GetGitHubContext("token");
|
||||||
|
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{accessToken}"));
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var accessToken = executionContext.GetGitHubContext("token");
|
// Intentionally empty. Temporary for GHES alpha release, download from dotcom unauthenticated.
|
||||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{accessToken}"));
|
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient.DefaultRequestHeaders.UserAgent.Add(HostContext.UserAgent);
|
httpClient.DefaultRequestHeaders.UserAgent.Add(HostContext.UserAgent);
|
||||||
@@ -610,7 +626,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
Trace.Verbose("Create watermark file indicate action download succeed.");
|
Trace.Verbose("Create watermark file indicate action download succeed.");
|
||||||
File.WriteAllText(destDirectory + ".completed", DateTime.UtcNow.ToString());
|
File.WriteAllText(watermarkFile, DateTime.UtcNow.ToString());
|
||||||
|
|
||||||
executionContext.Debug($"Archive '{archiveFile}' has been unzipped into '{destDirectory}'.");
|
executionContext.Debug($"Archive '{archiveFile}' has been unzipped into '{destDirectory}'.");
|
||||||
Trace.Info("Finished getting action repository.");
|
Trace.Info("Finished getting action repository.");
|
||||||
|
|||||||
@@ -111,6 +111,57 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Worker")]
|
||||||
|
public async void PrepareActions_SkipDownloadActionFromGraphWhenCached_OnPremises()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Setup();
|
||||||
|
var actionId = Guid.NewGuid();
|
||||||
|
var actions = new List<Pipelines.ActionStep>
|
||||||
|
{
|
||||||
|
new Pipelines.ActionStep()
|
||||||
|
{
|
||||||
|
Name = "action",
|
||||||
|
Id = actionId,
|
||||||
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
|
{
|
||||||
|
Name = "actions/no-such-action",
|
||||||
|
Ref = "master",
|
||||||
|
RepositoryType = "GitHub"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_configurationStore.Object.GetSettings().IsHostedServer = false;
|
||||||
|
var actionDirectory = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "actions/no-such-action", "master");
|
||||||
|
Directory.CreateDirectory(actionDirectory);
|
||||||
|
var watermarkFile = $"{actionDirectory}.completed";
|
||||||
|
File.WriteAllText(watermarkFile, DateTime.UtcNow.ToString());
|
||||||
|
var actionFile = Path.Combine(actionDirectory, "action.yml");
|
||||||
|
File.WriteAllText(actionFile, @"
|
||||||
|
name: ""no-such-action""
|
||||||
|
runs:
|
||||||
|
using: node12
|
||||||
|
main: no-such-action.js
|
||||||
|
");
|
||||||
|
var testFile = Path.Combine(actionDirectory, "test-file");
|
||||||
|
File.WriteAllText(testFile, "asdf");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(File.Exists(testFile));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Worker")]
|
[Trait("Category", "Worker")]
|
||||||
|
|||||||
Reference in New Issue
Block a user