2023-06-02 21:47:59 +02:00
using System ;
2019-10-10 00:52:42 -04:00
using System.Collections.Concurrent ;
using System.Collections.Generic ;
2020-04-23 17:02:13 -04:00
using System.Diagnostics ;
using System.Diagnostics.Tracing ;
2019-10-10 00:52:42 -04:00
using System.Globalization ;
using System.IO ;
using System.Linq ;
2020-04-23 17:02:13 -04:00
using System.Net.Http ;
using System.Net.Http.Headers ;
2019-10-10 00:52:42 -04:00
using System.Reflection ;
using System.Runtime.Loader ;
using System.Threading ;
using System.Threading.Tasks ;
using GitHub.DistributedTask.Logging ;
2022-06-10 15:51:20 +02:00
using GitHub.Runner.Common.Util ;
2019-10-10 00:52:42 -04:00
using GitHub.Runner.Sdk ;
2025-07-16 14:11:09 -04:00
using GitHub.Services.WebApi.Jwt ;
2019-10-10 00:52:42 -04:00
namespace GitHub.Runner.Common
{
public interface IHostContext : IDisposable
{
StartupType StartupType { get ; set ; }
CancellationToken RunnerShutdownToken { get ; }
ShutdownReason RunnerShutdownReason { get ; }
ISecretMasker SecretMasker { get ; }
2020-04-09 14:33:16 -04:00
List < ProductInfoHeaderValue > UserAgents { get ; }
2019-12-09 15:15:54 -05:00
RunnerWebProxy WebProxy { get ; }
2019-10-10 00:52:42 -04:00
string GetDirectory ( WellKnownDirectory directory );
string GetConfigFile ( WellKnownConfigFile configFile );
Tracing GetTrace ( string name );
Task Delay ( TimeSpan delay , CancellationToken cancellationToken );
T CreateService < T >() where T : class , IRunnerService ;
T GetService < T >() where T : class , IRunnerService ;
void SetDefaultCulture ( string name );
event EventHandler Unloading ;
void ShutdownRunner ( ShutdownReason reason );
void WritePerfCounter ( string counter );
2024-09-25 09:01:53 -04:00
void LoadDefaultUserAgents ();
2025-03-31 15:26:56 -04:00
bool AllowAuthMigration { get ; }
void EnableAuthMigration ( string trace );
void DeferAuthMigration ( TimeSpan deferred , string trace );
event EventHandler < AuthMigrationEventArgs > AuthMigrationChanged ;
2019-10-10 00:52:42 -04:00
}
public enum StartupType
{
Manual ,
Service ,
AutoStartup
}
public sealed class HostContext : EventListener , IObserver < DiagnosticListener >, IObserver < KeyValuePair < string , object >>, IHostContext , IDisposable
{
private const int _defaultLogPageSize = 8 ; //MB
private static int _defaultLogRetentionDays = 30 ;
private static int [] _vssHttpMethodEventIds = new int [] { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 24 };
private static int [] _vssHttpCredentialEventIds = new int [] { 11 , 13 , 14 , 15 , 16 , 17 , 18 , 20 , 21 , 22 , 27 , 29 };
2022-10-18 10:54:08 -04:00
private readonly ConcurrentDictionary < Type , object > _serviceInstances = new ();
private readonly ConcurrentDictionary < Type , Type > _serviceTypes = new ();
2019-10-10 00:52:42 -04:00
private readonly ISecretMasker _secretMasker = new SecretMasker ();
2022-10-18 10:54:08 -04:00
private readonly List < ProductInfoHeaderValue > _userAgents = new () { new ProductInfoHeaderValue ( $"GitHubActionsRunner-{BuildConstants.RunnerPackage.PackageName}" , BuildConstants . RunnerPackage . Version ) };
private CancellationTokenSource _runnerShutdownTokenSource = new ();
private object _perfLock = new ();
2019-10-10 00:52:42 -04:00
private Tracing _trace ;
2019-12-17 16:47:14 -05:00
private Tracing _actionsHttpTrace ;
private Tracing _netcoreHttpTrace ;
2019-10-10 00:52:42 -04:00
private ITraceManager _traceManager ;
private AssemblyLoadContext _loadContext ;
private IDisposable _httpTraceSubscription ;
private IDisposable _diagListenerSubscription ;
private StartupType _startupType ;
private string _perfFile ;
2022-10-18 10:54:08 -04:00
private RunnerWebProxy _webProxy = new ();
2024-09-25 09:01:53 -04:00
private string _hostType = string . Empty ;
2019-10-10 00:52:42 -04:00
2025-03-31 15:26:56 -04:00
// disable auth migration by default
private readonly ManualResetEventSlim _allowAuthMigration = new ManualResetEventSlim ( false );
private DateTime _deferredAuthMigrationTime = DateTime . MaxValue ;
private readonly object _authMigrationLock = new object ();
private CancellationTokenSource _authMigrationAutoReenableTaskCancellationTokenSource = new ();
private Task _authMigrationAutoReenableTask ;
2019-10-10 00:52:42 -04:00
public event EventHandler Unloading ;
2025-03-31 15:26:56 -04:00
public event EventHandler < AuthMigrationEventArgs > AuthMigrationChanged ;
2019-10-10 00:52:42 -04:00
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource . Token ;
public ShutdownReason RunnerShutdownReason { get ; private set ; }
public ISecretMasker SecretMasker => _secretMasker ;
2020-04-09 14:33:16 -04:00
public List < ProductInfoHeaderValue > UserAgents => _userAgents ;
2019-12-09 15:15:54 -05:00
public RunnerWebProxy WebProxy => _webProxy ;
2025-03-31 15:26:56 -04:00
public bool AllowAuthMigration => _allowAuthMigration . IsSet ;
2019-10-10 00:52:42 -04:00
public HostContext ( string hostType , string logFile = null )
{
// Validate args.
ArgUtil . NotNullOrEmpty ( hostType , nameof ( hostType ));
2024-09-25 09:01:53 -04:00
_hostType = hostType ;
2019-10-10 00:52:42 -04:00
_loadContext = AssemblyLoadContext . GetLoadContext ( typeof ( HostContext ). GetTypeInfo (). Assembly );
_loadContext . Unloading += LoadContext_Unloading ;
this . SecretMasker . AddValueEncoder ( ValueEncoders . Base64StringEscape );
this . SecretMasker . AddValueEncoder ( ValueEncoders . Base64StringEscapeShift1 );
this . SecretMasker . AddValueEncoder ( ValueEncoders . Base64StringEscapeShift2 );
2021-03-05 15:17:55 -06:00
this . SecretMasker . AddValueEncoder ( ValueEncoders . CommandLineArgumentEscape );
2019-10-10 00:52:42 -04:00
this . SecretMasker . AddValueEncoder ( ValueEncoders . ExpressionStringEscape );
this . SecretMasker . AddValueEncoder ( ValueEncoders . JsonStringEscape );
this . SecretMasker . AddValueEncoder ( ValueEncoders . UriDataEscape );
this . SecretMasker . AddValueEncoder ( ValueEncoders . XmlDataEscape );
2020-04-21 22:07:55 -04:00
this . SecretMasker . AddValueEncoder ( ValueEncoders . TrimDoubleQuotes );
2021-08-25 23:07:19 +02:00
this . SecretMasker . AddValueEncoder ( ValueEncoders . PowerShellPreAmpersandEscape );
this . SecretMasker . AddValueEncoder ( ValueEncoders . PowerShellPostAmpersandEscape );
2019-10-10 00:52:42 -04:00
2022-12-06 16:16:00 +01:00
// Create StdoutTraceListener if ENV is set
StdoutTraceListener stdoutTraceListener = null ;
if ( StringUtil . ConvertToBoolean ( Environment . GetEnvironmentVariable ( Constants . Variables . Agent . PrintLogToStdout )))
{
stdoutTraceListener = new StdoutTraceListener ( hostType );
}
2019-10-10 00:52:42 -04:00
// Create the trace manager.
if ( string . IsNullOrEmpty ( logFile ))
{
int logPageSize ;
string logSizeEnv = Environment . GetEnvironmentVariable ( $"{hostType.ToUpperInvariant()}_LOGSIZE" );
2021-12-02 23:04:39 +08:00
if ( string . IsNullOrEmpty ( logSizeEnv ) || ! int . TryParse ( logSizeEnv , out logPageSize ))
2019-10-10 00:52:42 -04:00
{
logPageSize = _defaultLogPageSize ;
}
int logRetentionDays ;
string logRetentionDaysEnv = Environment . GetEnvironmentVariable ( $"{hostType.ToUpperInvariant()}_LOGRETENTION" );
2021-12-02 23:04:39 +08:00
if ( string . IsNullOrEmpty ( logRetentionDaysEnv ) || ! int . TryParse ( logRetentionDaysEnv , out logRetentionDays ))
2019-10-10 00:52:42 -04:00
{
logRetentionDays = _defaultLogRetentionDays ;
}
// this should give us _diag folder under runner root directory
string diagLogDirectory = Path . Combine ( new DirectoryInfo ( Path . GetDirectoryName ( Assembly . GetEntryAssembly (). Location )). Parent . FullName , Constants . Path . DiagDirectory );
2022-12-06 16:16:00 +01:00
_traceManager = new TraceManager ( new HostTraceListener ( diagLogDirectory , hostType , logPageSize , logRetentionDays ), stdoutTraceListener , this . SecretMasker );
2019-10-10 00:52:42 -04:00
}
else
{
2022-12-06 16:16:00 +01:00
_traceManager = new TraceManager ( new HostTraceListener ( logFile ), stdoutTraceListener , this . SecretMasker );
2019-10-10 00:52:42 -04:00
}
_trace = GetTrace ( nameof ( HostContext ));
2019-12-17 16:47:14 -05:00
_actionsHttpTrace = GetTrace ( "GitHubActionsService" );
2019-10-10 00:52:42 -04:00
// Enable Http trace
bool enableHttpTrace ;
if ( bool . TryParse ( Environment . GetEnvironmentVariable ( "GITHUB_ACTIONS_RUNNER_HTTPTRACE" ), out enableHttpTrace ) && enableHttpTrace )
{
_trace . Warning ( "*****************************************************************************************" );
_trace . Warning ( "** **" );
_trace . Warning ( "** Http trace is enabled, all your http traffic will be dumped into runner diag log. **" );
_trace . Warning ( "** DO NOT share the log in public place! The trace may contains secrets in plain text. **" );
_trace . Warning ( "** **" );
_trace . Warning ( "*****************************************************************************************" );
2019-12-17 16:47:14 -05:00
_netcoreHttpTrace = GetTrace ( "HttpTrace" );
2019-10-10 00:52:42 -04:00
_diagListenerSubscription = DiagnosticListener . AllListeners . Subscribe ( this );
}
// Enable perf counter trace
string perfCounterLocation = Environment . GetEnvironmentVariable ( "RUNNER_PERFLOG" );
if (! string . IsNullOrEmpty ( perfCounterLocation ))
{
try
{
Directory . CreateDirectory ( perfCounterLocation );
_perfFile = Path . Combine ( perfCounterLocation , $"{hostType}.perf" );
}
catch ( Exception ex )
{
_trace . Error ( ex );
}
}
2019-12-09 15:15:54 -05:00
// Check and trace proxy info
if (! string . IsNullOrEmpty ( WebProxy . HttpProxyAddress ))
{
if ( string . IsNullOrEmpty ( WebProxy . HttpProxyUsername ) && string . IsNullOrEmpty ( WebProxy . HttpProxyPassword ))
{
_trace . Info ( $"Configuring anonymous proxy {WebProxy.HttpProxyAddress} for all HTTP requests." );
}
else
{
// Register proxy password as secret
if (! string . IsNullOrEmpty ( WebProxy . HttpProxyPassword ))
{
this . SecretMasker . AddValue ( WebProxy . HttpProxyPassword );
}
_trace . Info ( $"Configuring authenticated proxy {WebProxy.HttpProxyAddress} for all HTTP requests." );
}
}
if (! string . IsNullOrEmpty ( WebProxy . HttpsProxyAddress ))
{
if ( string . IsNullOrEmpty ( WebProxy . HttpsProxyUsername ) && string . IsNullOrEmpty ( WebProxy . HttpsProxyPassword ))
{
_trace . Info ( $"Configuring anonymous proxy {WebProxy.HttpsProxyAddress} for all HTTPS requests." );
}
else
{
// Register proxy password as secret
if (! string . IsNullOrEmpty ( WebProxy . HttpsProxyPassword ))
{
this . SecretMasker . AddValue ( WebProxy . HttpsProxyPassword );
}
_trace . Info ( $"Configuring authenticated proxy {WebProxy.HttpsProxyAddress} for all HTTPS requests." );
}
}
2024-09-25 09:01:53 -04:00
if ( StringUtil . ConvertToBoolean ( Environment . GetEnvironmentVariable ( "GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY" )))
{
_trace . Warning ( $"Runner is running under insecure mode: HTTPS server certificate validation has been turned off by GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY environment variable." );
}
LoadDefaultUserAgents ();
}
2025-03-31 15:26:56 -04:00
// marked as internal for testing
internal async Task AuthMigrationAuthReenableAsync ( TimeSpan refreshInterval , CancellationToken token )
{
try
{
while (! token . IsCancellationRequested )
{
_trace . Verbose ( $"Auth migration defer timer is set to expire at {_deferredAuthMigrationTime.ToString(" O ")}. AllowAuthMigration: {_allowAuthMigration.IsSet}." );
await Task . Delay ( refreshInterval , token );
if (! _allowAuthMigration . IsSet && DateTime . UtcNow > _deferredAuthMigrationTime )
{
_trace . Info ( $"Auth migration defer timer expired. Allowing auth migration." );
EnableAuthMigration ( "Auth migration defer timer expired." );
}
}
}
catch ( TaskCanceledException )
{
// Task was cancelled, exit the loop.
}
catch ( Exception ex )
{
_trace . Info ( "Error in auth migration reenable task." );
_trace . Error ( ex );
}
}
public void EnableAuthMigration ( string trace )
{
_allowAuthMigration . Set ();
lock ( _authMigrationLock )
{
if ( _authMigrationAutoReenableTask == null )
{
var refreshIntervalInMS = 60 * 1000 ;
#if DEBUG
// For L0, we will refresh faster
if (! string . IsNullOrEmpty ( Environment . GetEnvironmentVariable ( "_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL" )))
{
refreshIntervalInMS = int . Parse ( Environment . GetEnvironmentVariable ( "_GITHUB_ACTION_AUTH_MIGRATION_REFRESH_INTERVAL" ));
}
#endif
_authMigrationAutoReenableTask = AuthMigrationAuthReenableAsync ( TimeSpan . FromMilliseconds ( refreshIntervalInMS ), _authMigrationAutoReenableTaskCancellationTokenSource . Token );
}
}
2025-04-23 16:57:54 -04:00
_trace . Info ( $"Enable auth migration at {DateTime.UtcNow.ToString(" O ")}." );
2025-03-31 15:26:56 -04:00
AuthMigrationChanged ?. Invoke ( this , new AuthMigrationEventArgs ( trace ));
}
public void DeferAuthMigration ( TimeSpan deferred , string trace )
{
_allowAuthMigration . Reset ();
// defer migration for a while
lock ( _authMigrationLock )
{
_deferredAuthMigrationTime = DateTime . UtcNow . Add ( deferred );
}
_trace . Info ( $"Disabled auth migration until {_deferredAuthMigrationTime.ToString(" O ")}." );
AuthMigrationChanged ?. Invoke ( this , new AuthMigrationEventArgs ( trace ));
}
2024-09-25 09:01:53 -04:00
public void LoadDefaultUserAgents ()
{
2019-12-09 15:15:54 -05:00
if ( string . IsNullOrEmpty ( WebProxy . HttpProxyAddress ) && string . IsNullOrEmpty ( WebProxy . HttpsProxyAddress ))
{
_trace . Info ( $"No proxy settings were found based on environmental variables (http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY)" );
}
2023-11-28 09:43:13 -05:00
else
{
_userAgents . Add ( new ProductInfoHeaderValue ( "HttpProxyConfigured" , bool . TrueString ));
}
2020-04-09 14:33:16 -04:00
var credFile = GetConfigFile ( WellKnownConfigFile . Credentials );
if ( File . Exists ( credFile ))
{
var credData = IOUtil . LoadObject < CredentialData >( credFile );
if ( credData != null &&
credData . Data . TryGetValue ( "clientId" , out var clientId ))
{
2021-11-15 13:42:57 -06:00
_userAgents . Add ( new ProductInfoHeaderValue ( "ClientId" , clientId ));
2020-04-09 14:33:16 -04:00
}
2025-07-16 14:11:09 -04:00
// for Hosted runner, we can pull orchestrationId from JWT claims of the runner listening token.
if ( credData != null &&
credData . Scheme == Constants . Configuration . OAuthAccessToken &&
credData . Data . TryGetValue ( Constants . Runner . CommandLine . Args . Token , out var accessToken ) &&
! string . IsNullOrEmpty ( accessToken ))
{
try
{
var jwt = JsonWebToken . Create ( accessToken );
var claims = jwt . ExtractClaims ();
var orchestrationId = claims . FirstOrDefault ( x => string . Equals ( x . Type , "orch_id" , StringComparison . OrdinalIgnoreCase ))?. Value ;
if ( string . IsNullOrEmpty ( orchestrationId ))
{
// fallback to orchid for C# actions-service
orchestrationId = claims . FirstOrDefault ( x => string . Equals ( x . Type , "orchid" , StringComparison . OrdinalIgnoreCase ))?. Value ;
}
if (! string . IsNullOrEmpty ( orchestrationId ))
{
_trace . Info ( $"Pull OrchestrationId {orchestrationId} from runner JWT claims" );
_userAgents . Insert ( 0 , new ProductInfoHeaderValue ( "OrchestrationId" , orchestrationId ));
}
}
catch ( Exception ex )
{
_trace . Error ( "Fail to extract OrchestrationId from runner JWT claims" );
_trace . Error ( ex );
}
}
2020-04-09 14:33:16 -04:00
}
2021-11-15 13:42:57 -06:00
var runnerFile = GetConfigFile ( WellKnownConfigFile . Runner );
if ( File . Exists ( runnerFile ))
{
2023-03-30 15:39:09 +02:00
var runnerSettings = IOUtil . LoadObject < RunnerSettings >( runnerFile , true );
2021-11-15 13:42:57 -06:00
_userAgents . Add ( new ProductInfoHeaderValue ( "RunnerId" , runnerSettings . AgentId . ToString ( CultureInfo . InvariantCulture )));
_userAgents . Add ( new ProductInfoHeaderValue ( "GroupId" , runnerSettings . PoolId . ToString ( CultureInfo . InvariantCulture )));
}
2022-02-17 15:01:48 -05:00
_userAgents . Add ( new ProductInfoHeaderValue ( "CommitSHA" , BuildConstants . Source . CommitHash ));
2023-01-16 10:18:55 -05:00
var extraUserAgent = Environment . GetEnvironmentVariable ( "GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT" );
if (! string . IsNullOrEmpty ( extraUserAgent ))
{
var extraUserAgentSplit = extraUserAgent . Split ( '/' , StringSplitOptions . RemoveEmptyEntries );
if ( extraUserAgentSplit . Length != 2 )
{
_trace . Error ( $"GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT is not in the format of 'name/version'." );
}
var extraUserAgentHeader = new ProductInfoHeaderValue ( extraUserAgentSplit [ 0 ], extraUserAgentSplit [ 1 ]);
_trace . Info ( $"Adding extra user agent '{extraUserAgentHeader}' to all HTTP requests." );
_userAgents . Add ( extraUserAgentHeader );
}
2024-08-16 15:17:13 -04:00
var currentProcess = Process . GetCurrentProcess ();
_userAgents . Add ( new ProductInfoHeaderValue ( "Pid" , currentProcess . Id . ToString ()));
_userAgents . Add ( new ProductInfoHeaderValue ( "CreationTime" , Uri . EscapeDataString ( DateTime . UtcNow . ToString ( "O" ))));
2024-09-25 09:01:53 -04:00
_userAgents . Add ( new ProductInfoHeaderValue ( $"({_hostType})" ));
2019-10-10 00:52:42 -04:00
}
public string GetDirectory ( WellKnownDirectory directory )
{
string path ;
switch ( directory )
{
case WellKnownDirectory . Bin :
path = Path . GetDirectoryName ( Assembly . GetEntryAssembly (). Location );
break ;
case WellKnownDirectory . Diag :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
Constants . Path . DiagDirectory );
break ;
case WellKnownDirectory . Externals :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
Constants . Path . ExternalsDirectory );
break ;
case WellKnownDirectory . Root :
path = new DirectoryInfo ( GetDirectory ( WellKnownDirectory . Bin )). Parent . FullName ;
break ;
case WellKnownDirectory . Temp :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Work ),
Constants . Path . TempDirectory );
break ;
case WellKnownDirectory . Actions :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Work ),
Constants . Path . ActionsDirectory );
break ;
case WellKnownDirectory . Tools :
2019-12-17 18:21:13 -05:00
// TODO: Coallesce to just check RUNNER_TOOL_CACHE when images stabilize
path = Environment . GetEnvironmentVariable ( "RUNNER_TOOL_CACHE" ) ?? Environment . GetEnvironmentVariable ( "RUNNER_TOOLSDIRECTORY" ) ?? Environment . GetEnvironmentVariable ( "AGENT_TOOLSDIRECTORY" ) ?? Environment . GetEnvironmentVariable ( Constants . Variables . Agent . ToolsDirectory );
2019-10-10 00:52:42 -04:00
if ( string . IsNullOrEmpty ( path ))
{
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Work ),
Constants . Path . ToolDirectory );
}
break ;
case WellKnownDirectory . Update :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Work ),
Constants . Path . UpdateDirectory );
break ;
case WellKnownDirectory . Work :
var configurationStore = GetService < IConfigurationStore >();
RunnerSettings settings = configurationStore . GetSettings ();
ArgUtil . NotNull ( settings , nameof ( settings ));
ArgUtil . NotNullOrEmpty ( settings . WorkFolder , nameof ( settings . WorkFolder ));
path = Path . GetFullPath ( Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
settings . WorkFolder ));
break ;
default :
throw new NotSupportedException ( $"Unexpected well known directory: '{directory}'" );
}
_trace . Info ( $"Well known directory '{directory}': '{path}'" );
return path ;
}
public string GetConfigFile ( WellKnownConfigFile configFile )
{
string path ;
switch ( configFile )
{
case WellKnownConfigFile . Runner :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".runner" );
break ;
2025-03-19 12:37:08 -04:00
case WellKnownConfigFile . MigratedRunner :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".runner_migrated" );
break ;
2019-10-10 00:52:42 -04:00
case WellKnownConfigFile . Credentials :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".credentials" );
break ;
2020-03-04 21:40:58 -05:00
case WellKnownConfigFile . MigratedCredentials :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".credentials_migrated" );
break ;
2019-10-10 00:52:42 -04:00
case WellKnownConfigFile . RSACredentials :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".credentials_rsaparams" );
break ;
case WellKnownConfigFile . Service :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".service" );
break ;
case WellKnownConfigFile . CredentialStore :
#if OS_OSX
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".credential_store.keychain" );
#else
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".credential_store" );
#endif
break ;
case WellKnownConfigFile . Certificates :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".certificates" );
break ;
case WellKnownConfigFile . Options :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".options" );
break ;
2020-03-11 10:36:56 -04:00
case WellKnownConfigFile . SetupInfo :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Root ),
".setup_info" );
break ;
2022-01-19 10:31:17 -05:00
2021-09-29 15:49:31 -04:00
case WellKnownConfigFile . Telemetry :
path = Path . Combine (
GetDirectory ( WellKnownDirectory . Diag ),
".telemetry" );
break ;
2020-03-11 10:36:56 -04:00
2019-10-10 00:52:42 -04:00
default :
throw new NotSupportedException ( $"Unexpected well known config file: '{configFile}'" );
}
_trace . Info ( $"Well known config file '{configFile}': '{path}'" );
return path ;
}
public Tracing GetTrace ( string name )
{
return _traceManager [ name ];
}
public async Task Delay ( TimeSpan delay , CancellationToken cancellationToken )
{
await Task . Delay ( delay , cancellationToken );
}
/// <summary>
/// Creates a new instance of T.
/// </summary>
public T CreateService < T >() where T : class , IRunnerService
{
Type target ;
if (! _serviceTypes . TryGetValue ( typeof ( T ), out target ))
{
// Infer the concrete type from the ServiceLocatorAttribute.
CustomAttributeData attribute = typeof ( T )
. GetTypeInfo ()
. CustomAttributes
. FirstOrDefault ( x => x . AttributeType == typeof ( ServiceLocatorAttribute ));
if ( attribute != null )
{
foreach ( CustomAttributeNamedArgument arg in attribute . NamedArguments )
{
if ( string . Equals ( arg . MemberName , ServiceLocatorAttribute . DefaultPropertyName , StringComparison . Ordinal ))
{
target = arg . TypedValue . Value as Type ;
}
}
}
if ( target == null )
{
throw new KeyNotFoundException ( string . Format ( CultureInfo . InvariantCulture , "Service mapping not found for key '{0}'." , typeof ( T ). FullName ));
}
_serviceTypes . TryAdd ( typeof ( T ), target );
target = _serviceTypes [ typeof ( T )];
}
// Create a new instance.
T svc = Activator . CreateInstance ( target ) as T ;
svc . Initialize ( this );
return svc ;
}
/// <summary>
/// Gets or creates an instance of T.
/// </summary>
public T GetService < T >() where T : class , IRunnerService
{
// Return the cached instance if one already exists.
object instance ;
if ( _serviceInstances . TryGetValue ( typeof ( T ), out instance ))
{
return instance as T ;
}
// Otherwise create a new instance and try to add it to the cache.
_serviceInstances . TryAdd ( typeof ( T ), CreateService < T >());
// Return the instance from the cache.
return _serviceInstances [ typeof ( T )] as T ;
}
public void SetDefaultCulture ( string name )
{
ArgUtil . NotNull ( name , nameof ( name ));
_trace . Verbose ( $"Setting default culture and UI culture to: '{name}'" );
CultureInfo . DefaultThreadCurrentCulture = new CultureInfo ( name );
CultureInfo . DefaultThreadCurrentUICulture = new CultureInfo ( name );
}
public void ShutdownRunner ( ShutdownReason reason )
{
ArgUtil . NotNull ( reason , nameof ( reason ));
_trace . Info ( $"Runner will be shutdown for {reason.ToString()}" );
RunnerShutdownReason = reason ;
_runnerShutdownTokenSource . Cancel ();
}
public override void Dispose ()
{
Dispose ( true );
GC . SuppressFinalize ( this );
}
public StartupType StartupType
{
get
{
return _startupType ;
}
set
{
_startupType = value ;
}
}
public void WritePerfCounter ( string counter )
{
if (! string . IsNullOrEmpty ( _perfFile ))
{
string normalizedCounter = counter . Replace ( ':' , '_' );
lock ( _perfLock )
{
try
{
File . AppendAllLines ( _perfFile , new [] { $"{normalizedCounter}:{DateTime.UtcNow.ToString(" O ")}" });
}
catch ( Exception ex )
{
_trace . Error ( ex );
}
}
}
}
private void Dispose ( bool disposing )
{
// TODO: Dispose the trace listener also.
if ( disposing )
{
if ( _loadContext != null )
{
_loadContext . Unloading -= LoadContext_Unloading ;
_loadContext = null ;
}
2025-03-31 15:26:56 -04:00
if ( _authMigrationAutoReenableTask != null )
{
_authMigrationAutoReenableTaskCancellationTokenSource ?. Cancel ();
}
if ( _authMigrationAutoReenableTaskCancellationTokenSource != null )
{
_authMigrationAutoReenableTaskCancellationTokenSource ?. Dispose ();
_authMigrationAutoReenableTaskCancellationTokenSource = null ;
}
2019-10-10 00:52:42 -04:00
_httpTraceSubscription ?. Dispose ();
_diagListenerSubscription ?. Dispose ();
_traceManager ?. Dispose ();
_traceManager = null ;
_runnerShutdownTokenSource ?. Dispose ();
_runnerShutdownTokenSource = null ;
base . Dispose ();
}
}
private void LoadContext_Unloading ( AssemblyLoadContext obj )
{
if ( Unloading != null )
{
Unloading ( this , null );
}
}
void IObserver < DiagnosticListener >. OnCompleted ()
{
2019-12-17 16:47:14 -05:00
_netcoreHttpTrace . Info ( "DiagListeners finished transmitting data." );
2019-10-10 00:52:42 -04:00
}
void IObserver < DiagnosticListener >. OnError ( Exception error )
{
2019-12-17 16:47:14 -05:00
_netcoreHttpTrace . Error ( error );
2019-10-10 00:52:42 -04:00
}
void IObserver < DiagnosticListener >. OnNext ( DiagnosticListener listener )
{
if ( listener . Name == "HttpHandlerDiagnosticListener" && _httpTraceSubscription == null )
{
_httpTraceSubscription = listener . Subscribe ( this );
}
}
void IObserver < KeyValuePair < string , object >>. OnCompleted ()
{
2019-12-17 16:47:14 -05:00
_netcoreHttpTrace . Info ( "HttpHandlerDiagnosticListener finished transmitting data." );
2019-10-10 00:52:42 -04:00
}
void IObserver < KeyValuePair < string , object >>. OnError ( Exception error )
{
2019-12-17 16:47:14 -05:00
_netcoreHttpTrace . Error ( error );
2019-10-10 00:52:42 -04:00
}
void IObserver < KeyValuePair < string , object >>. OnNext ( KeyValuePair < string , object > value )
{
2019-12-17 16:47:14 -05:00
_netcoreHttpTrace . Info ( $"Trace {value.Key} event:{Environment.NewLine}{value.Value.ToString()}" );
2019-10-10 00:52:42 -04:00
}
protected override void OnEventSourceCreated ( EventSource source )
{
2019-12-17 16:47:14 -05:00
if ( source . Name . Equals ( "GitHub-Actions-Http" ))
2019-10-10 00:52:42 -04:00
{
EnableEvents ( source , EventLevel . Verbose );
}
}
protected override void OnEventWritten ( EventWrittenEventArgs eventData )
{
if ( eventData == null )
{
return ;
}
string message = eventData . Message ;
object [] payload = new object [ 0 ];
if ( eventData . Payload != null && eventData . Payload . Count > 0 )
{
payload = eventData . Payload . ToArray ();
}
try
{
if ( _vssHttpMethodEventIds . Contains ( eventData . EventId ))
{
payload [ 0 ] = Enum . Parse ( typeof ( VssHttpMethod ), (( int ) payload [ 0 ]). ToString ());
}
else if ( _vssHttpCredentialEventIds . Contains ( eventData . EventId ))
{
payload [ 0 ] = Enum . Parse ( typeof ( GitHub . Services . Common . VssCredentialsType ), (( int ) payload [ 0 ]). ToString ());
}
2024-11-25 15:44:03 -05:00
if ( payload . Length > 0 && ! string . IsNullOrEmpty ( eventData . Message ))
2019-10-10 00:52:42 -04:00
{
message = String . Format ( eventData . Message . Replace ( "%n" , Environment . NewLine ), payload );
}
switch ( eventData . Level )
{
case EventLevel . Critical :
case EventLevel . Error :
2019-12-17 16:47:14 -05:00
_actionsHttpTrace . Error ( message );
2019-10-10 00:52:42 -04:00
break ;
case EventLevel . Warning :
2019-12-17 16:47:14 -05:00
_actionsHttpTrace . Warning ( message );
2019-10-10 00:52:42 -04:00
break ;
case EventLevel . Informational :
2019-12-17 16:47:14 -05:00
_actionsHttpTrace . Info ( message );
2019-10-10 00:52:42 -04:00
break ;
default :
2019-12-17 16:47:14 -05:00
_actionsHttpTrace . Verbose ( message );
2019-10-10 00:52:42 -04:00
break ;
}
}
catch ( Exception ex )
{
2019-12-17 16:47:14 -05:00
_actionsHttpTrace . Error ( ex );
_actionsHttpTrace . Info ( eventData . Message );
_actionsHttpTrace . Info ( string . Join ( ", " , eventData . Payload ?. ToArray () ?? new string [ 0 ]));
2019-10-10 00:52:42 -04:00
}
}
// Copied from pipelines server code base, used for EventData translation.
internal enum VssHttpMethod
{
UNKNOWN ,
DELETE ,
HEAD ,
GET ,
OPTIONS ,
PATCH ,
POST ,
PUT ,
}
}
public static class HostContextExtension
{
public static HttpClientHandler CreateHttpClientHandler ( this IHostContext context )
{
2020-04-23 17:02:13 -04:00
var handlerFactory = context . GetService < IHttpClientHandlerFactory >();
return handlerFactory . CreateClientHandler ( context . WebProxy );
2019-10-10 00:52:42 -04:00
}
2022-06-10 15:51:20 +02:00
public static string GetDefaultShellForScript ( this IHostContext hostContext , string path , string prependPath )
{
var trace = hostContext . GetTrace ( nameof ( GetDefaultShellForScript ));
switch ( Path . GetExtension ( path ))
{
case ".sh" :
// use 'sh' args but prefer bash
if ( WhichUtil . Which ( "bash" , false , trace , prependPath ) != null )
{
return "bash" ;
}
return "sh" ;
case ".ps1" :
if ( WhichUtil . Which ( "pwsh" , false , trace , prependPath ) != null )
{
return "pwsh" ;
}
return "powershell" ;
case ".js" :
return Path . Combine ( hostContext . GetDirectory ( WellKnownDirectory . Externals ), NodeUtil . GetInternalNodeVersion (), "bin" , $"node{IOUtil.ExeExtension}" ) + " {0}" ;
default :
throw new ArgumentException ( $"{path} is not a valid path to a script. Make sure it ends in '.sh', '.ps1' or '.js'." );
}
}
2019-10-10 00:52:42 -04:00
}
public enum ShutdownReason
{
UserCancelled = 0 ,
OperatingSystemShutdown = 1 ,
}
}