mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
c
This commit is contained in:
@@ -46,6 +46,7 @@ namespace GitHub.Runner.Common
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.SetOutputCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.SaveStateCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.AddPathCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.RefreshTokenCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.AddMaskCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.AddMatcherCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.RemoveMatcherCommandExtension, Runner.Worker");
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace GitHub.Runner.Common
|
||||
Task<List<TimelineRecord>> UpdateTimelineRecordsAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken);
|
||||
Task RaisePlanEventAsync<T>(Guid scopeIdentifier, string hubName, Guid planId, T eventData, CancellationToken cancellationToken) where T : JobEvent;
|
||||
Task<Timeline> GetTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken);
|
||||
Task<GitHubToken> RefreshGitHubTokenAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid jobId, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed class JobServer : RunnerService, IJobServer
|
||||
@@ -113,5 +114,11 @@ namespace GitHub.Runner.Common
|
||||
CheckConnection();
|
||||
return _taskClient.GetTimelineAsync(scopeIdentifier, hubName, planId, timelineId, includeRecords: true, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task<GitHubToken> RefreshGitHubTokenAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid jobId, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckConnection();
|
||||
return _taskClient.RefreshTokenAsync(scopeIdentifier, hubName, planId, jobId, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +288,30 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RefreshTokenCommandExtension : RunnerService, IActionCommandExtension
|
||||
{
|
||||
public string Command => "refresh-token";
|
||||
public bool OmitEcho => false;
|
||||
|
||||
public Type ExtensionType => typeof(IActionCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
{
|
||||
ArgUtil.NotNullOrEmpty(command.Data, "token file");
|
||||
var runnerTemp = HostContext.GetDirectory(WellKnownDirectory.Temp);
|
||||
var tempFile = Path.Combine(runnerTemp, command.Data);
|
||||
if (!tempFile.StartsWith(runnerTemp + Path.DirectorySeparatorChar))
|
||||
{
|
||||
throw new Exception($"'{command.Data}' has to be under runner temp directory '{runnerTemp}'");
|
||||
}
|
||||
|
||||
Trace.Info("Here");
|
||||
var githubToken = context.GetGitHubToken().GetAwaiter().GetResult();
|
||||
File.WriteAllText(tempFile, githubToken);
|
||||
Trace.Info("HereAgain");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AddMatcherCommandExtension : RunnerService, IActionCommandExtension
|
||||
{
|
||||
public string Command => "add-matcher";
|
||||
|
||||
@@ -21,6 +21,7 @@ using System.Collections;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -99,6 +100,8 @@ namespace GitHub.Runner.Worker
|
||||
// others
|
||||
void ForceTaskComplete();
|
||||
void RegisterPostJobStep(IStep step);
|
||||
Task<string> GetGitHubToken();
|
||||
Task UpdateGitHubTokenInContext();
|
||||
}
|
||||
|
||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||
@@ -110,6 +113,7 @@ namespace GitHub.Runner.Worker
|
||||
private readonly object _loggerLock = new object();
|
||||
private readonly HashSet<string> _outputvariables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object _matchersLock = new object();
|
||||
public readonly List<Task> _getGitHubTokenTasks = new List<Task>();
|
||||
|
||||
private event OnMatcherChanged _onMatcherChanged;
|
||||
|
||||
@@ -130,6 +134,13 @@ namespace GitHub.Runner.Worker
|
||||
// only job level ExecutionContext will track throttling delay.
|
||||
private long _totalThrottlingDelayInMilliseconds = 0;
|
||||
|
||||
private Guid _jobId;
|
||||
private Guid _scopeIdentifier;
|
||||
private string _hubName;
|
||||
private Guid _planId;
|
||||
|
||||
private Stopwatch _githubTokenExpireTimer = new Stopwatch();
|
||||
|
||||
public Guid Id => _record.Id;
|
||||
public string ScopeName { get; private set; }
|
||||
public string ContextName { get; private set; }
|
||||
@@ -242,6 +253,59 @@ namespace GitHub.Runner.Worker
|
||||
});
|
||||
}
|
||||
|
||||
public Task<string> GetGitHubToken()
|
||||
{
|
||||
Trace.Info($"Try get new GITHUB_TOKEN");
|
||||
return Root.RefreshGitHubToken();
|
||||
}
|
||||
|
||||
public async Task UpdateGitHubTokenInContext()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Root.EnsureGitHubToken();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Warning($"Fail to get a new GITHUB_TOKEN, error: {ex.Message}");
|
||||
this.Debug(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> RefreshGitHubToken()
|
||||
{
|
||||
Trace.Info("Request new GITHUB_TOKEN");
|
||||
var jobServer = HostContext.GetService<IJobServer>();
|
||||
var githubToken = await jobServer.RefreshGitHubTokenAsync(_scopeIdentifier, _hubName, _planId, _jobId, CancellationToken.None);
|
||||
|
||||
if (!string.IsNullOrEmpty(githubToken?.Token))
|
||||
{
|
||||
// register secret masker
|
||||
Trace.Info("Register secret masker for new GITHUB_TOKEN");
|
||||
HostContext.SecretMasker.AddValue(githubToken.Token);
|
||||
return githubToken.Token;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Get empty GTIHUB_TOKEN.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EnsureGitHubToken()
|
||||
{
|
||||
// needs to refresh GITHUB_TOKEN every 50 mins since the token is good for 60 min by default
|
||||
if (_githubTokenExpireTimer.Elapsed.TotalMilliseconds > 10)
|
||||
{
|
||||
var githubToken = await this.RefreshGitHubToken();
|
||||
var secretsContext = ExpressionValues["secrets"] as DictionaryContextData;
|
||||
secretsContext["GITHUB_TOKEN"] = new StringContextData(githubToken);
|
||||
var githubContext = ExpressionValues["github"] as GitHubContext;
|
||||
githubContext["token"] = new StringContextData(githubToken);
|
||||
|
||||
_githubTokenExpireTimer.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterPostJobStep(IStep step)
|
||||
{
|
||||
if (step is IActionRunner actionRunner && !Root.StepsWithPostRegisted.Add(actionRunner.Action.Id))
|
||||
@@ -617,6 +681,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
ExpressionValues["env"] = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
_githubTokenExpireTimer.Start();
|
||||
|
||||
// Prepend Path
|
||||
PrependPath = new List<string>();
|
||||
@@ -630,6 +695,11 @@ namespace GitHub.Runner.Worker
|
||||
// StepsWithPostRegisted for job ExecutionContext
|
||||
StepsWithPostRegisted = new HashSet<Guid>();
|
||||
|
||||
_scopeIdentifier = message.Plan.ScopeIdentifier;
|
||||
_hubName = message.Plan.PlanType;
|
||||
_jobId = message.JobId;
|
||||
_planId = message.Plan.PlanId;
|
||||
|
||||
// Job timeline record.
|
||||
InitializeTimelineRecord(
|
||||
timelineId: message.Timeline.Id,
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// Start
|
||||
step.ExecutionContext.Start();
|
||||
await step.ExecutionContext.UpdateGitHubTokenInContext();
|
||||
|
||||
// Initialize scope
|
||||
if (InitializeScope(step, scopeInputs))
|
||||
|
||||
@@ -317,5 +317,40 @@ namespace GitHub.DistributedTask.WebApi
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [Preview API]
|
||||
/// </summary>
|
||||
/// <param name="scopeIdentifier">The project GUID to scope the request</param>
|
||||
/// <param name="hubName">The name of the server hub: "build" for the Build server or "rm" for the Release Management server</param>
|
||||
/// <param name="planId"></param>
|
||||
/// <param name="jobId"></param>
|
||||
/// <param name="userState"></param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
public virtual Task<GitHubToken> RefreshTokenAsync(
|
||||
Guid scopeIdentifier,
|
||||
string hubName,
|
||||
Guid planId,
|
||||
Guid jobId,
|
||||
object userState = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HttpMethod httpMethod = new HttpMethod("GET");
|
||||
Guid locationId = new Guid("8aa8aff7-751b-496e-be8d-b7818770efb3");
|
||||
object routeValues = new { scopeIdentifier = scopeIdentifier, hubName = hubName, planId = planId };
|
||||
|
||||
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
|
||||
queryParams.Add("jobId", jobId.ToString());
|
||||
|
||||
return SendAsync<GitHubToken>(
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 1),
|
||||
queryParameters: queryParams,
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,17 @@ using System.Runtime.Serialization;
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
|
||||
[DataContract]
|
||||
public class GitHubToken
|
||||
{
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string Token { get; set; }
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public DateTime Expires_at { get; set; }
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class Issue
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user