diff --git a/src/Runner.Common/ConfigurationStore.cs b/src/Runner.Common/ConfigurationStore.cs index b3cb26a86..7daa4d66f 100644 --- a/src/Runner.Common/ConfigurationStore.cs +++ b/src/Runner.Common/ConfigurationStore.cs @@ -77,14 +77,15 @@ namespace GitHub.Runner.Common Uri accountUri = new(this.ServerUrl); string repoOrOrgName = string.Empty; - if (accountUri.Host.EndsWith(".githubusercontent.com", StringComparison.OrdinalIgnoreCase)) + if (accountUri.Host.EndsWith(".githubusercontent.com", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(this.GitHubUrl)) { Uri gitHubUrl = new(this.GitHubUrl); // Use the "NWO part" from the GitHub URL path repoOrOrgName = gitHubUrl.AbsolutePath.Trim('/'); } - else + + if (string.IsNullOrEmpty(repoOrOrgName)) { repoOrOrgName = accountUri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); } diff --git a/src/Runner.Common/Constants.cs b/src/Runner.Common/Constants.cs index 33ab3ce04..2c3a6e299 100644 --- a/src/Runner.Common/Constants.cs +++ b/src/Runner.Common/Constants.cs @@ -128,6 +128,7 @@ namespace GitHub.Runner.Common public static readonly string Check = "check"; public static readonly string Commit = "commit"; public static readonly string Ephemeral = "ephemeral"; + public static readonly string GenerateServiceConfig = "generateServiceConfig"; public static readonly string Help = "help"; public static readonly string Replace = "replace"; public static readonly string DisableUpdate = "disableupdate"; diff --git a/src/Runner.Listener/CommandSettings.cs b/src/Runner.Listener/CommandSettings.cs index 44039bb5c..4d00f1d45 100644 --- a/src/Runner.Listener/CommandSettings.cs +++ b/src/Runner.Listener/CommandSettings.cs @@ -34,6 +34,7 @@ namespace GitHub.Runner.Listener { Constants.Runner.CommandLine.Flags.DisableUpdate, Constants.Runner.CommandLine.Flags.Ephemeral, + Constants.Runner.CommandLine.Flags.GenerateServiceConfig, Constants.Runner.CommandLine.Flags.Replace, Constants.Runner.CommandLine.Flags.RunAsService, Constants.Runner.CommandLine.Flags.Unattended, @@ -79,11 +80,12 @@ namespace GitHub.Runner.Listener // Flags. public bool Check => TestFlag(Constants.Runner.CommandLine.Flags.Check); public bool Commit => TestFlag(Constants.Runner.CommandLine.Flags.Commit); + public bool DisableUpdate => TestFlag(Constants.Runner.CommandLine.Flags.DisableUpdate); + public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral); + public bool GenerateServiceConfig => TestFlag(Constants.Runner.CommandLine.Flags.GenerateServiceConfig); public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help); public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended); public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version); - public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral); - public bool DisableUpdate => TestFlag(Constants.Runner.CommandLine.Flags.DisableUpdate); // Keep this around since customers still relies on it public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once); diff --git a/src/Runner.Listener/Configuration/ConfigurationManager.cs b/src/Runner.Listener/Configuration/ConfigurationManager.cs index 72b574873..25217f9be 100644 --- a/src/Runner.Listener/Configuration/ConfigurationManager.cs +++ b/src/Runner.Listener/Configuration/ConfigurationManager.cs @@ -81,6 +81,27 @@ namespace GitHub.Runner.Listener.Configuration _term.WriteLine("--------------------------------------------------------------------------------"); Trace.Info(nameof(ConfigureAsync)); + + if (command.GenerateServiceConfig) + { +#if OS_LINUX + if (!IsConfigured()) + { + throw new InvalidOperationException("--generateServiceConfig requires that the runner is already configured. For configuring a new runner as a service, run './config.sh'."); + } + + RunnerSettings settings = _store.GetSettings(); + + Trace.Info($"generate service config for runner: {settings.AgentId}"); + var controlManager = HostContext.GetService(); + controlManager.GenerateScripts(settings); + + return; +#else + throw new NotSupportedException("--generateServiceConfig is only supported on Linux."); +#endif + } + if (IsConfigured()) { throw new InvalidOperationException("Cannot configure the runner because it is already configured. To reconfigure the runner, run 'config.cmd remove' or './config.sh remove' first."); diff --git a/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs b/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs index a3fdd5603..ca6e90b6b 100644 --- a/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs +++ b/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs @@ -234,5 +234,103 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration _runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny(), It.Is(p => p == TaskAgentPoolType.Automation)), Times.Exactly(1)); } } + +#if OS_LINUX + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "ConfigurationManagement")] + public async Task ConfigureRunnerServiceFailsOnUnconfiguredRunners() + { + using (TestHostContext tc = CreateTestContext()) + { + Tracing trace = tc.GetTrace(); + + trace.Info("Creating config manager"); + IConfigurationManager configManager = new ConfigurationManager(); + configManager.Initialize(tc); + + trace.Info("Preparing command line arguments"); + var command = new CommandSettings( + tc, + new[] + { + "configure", + "--generateServiceConfig", + }); + trace.Info("Constructed"); + _store.Setup(x => x.IsConfigured()).Returns(false); + + trace.Info("Ensuring service generation mode fails when on un-configured runners"); + var ex = await Assert.ThrowsAsync(() => configManager.ConfigureAsync(command)); + + Assert.Contains("requires that the runner is already configured", ex.Message); + } + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "ConfigurationManagement")] + public async Task ConfigureRunnerServiceCreatesService() + { + using (TestHostContext tc = CreateTestContext()) + { + Tracing trace = tc.GetTrace(); + + trace.Info("Creating config manager"); + IConfigurationManager configManager = new ConfigurationManager(); + configManager.Initialize(tc); + + trace.Info("Preparing command line arguments"); + var command = new CommandSettings( + tc, + new[] + { + "configure", + "--generateServiceConfig", + }); + trace.Info("Constructed"); + + _store.Setup(x => x.IsConfigured()).Returns(true); + + trace.Info("Ensuring service generation mode fails when on un-configured runners"); + await configManager.ConfigureAsync(command); + + _serviceControlManager.Verify(x => x.GenerateScripts(It.IsAny()), Times.Once); + } + } +#endif + +#if !OS_LINUX + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "ConfigurationManagement")] + public async Task ConfigureRunnerServiceFailsOnUnsupportedPlatforms() + { + using (TestHostContext tc = CreateTestContext()) + { + Tracing trace = tc.GetTrace(); + + trace.Info("Creating config manager"); + IConfigurationManager configManager = new ConfigurationManager(); + configManager.Initialize(tc); + + trace.Info("Preparing command line arguments"); + var command = new CommandSettings( + tc, + new[] + { + "configure", + "--generateServiceConfig", + }); + trace.Info("Constructed"); + _store.Setup(x => x.IsConfigured()).Returns(true); + + trace.Info("Ensuring service generation mode fails on unsupported runner platforms"); + var ex = await Assert.ThrowsAsync(() => configManager.ConfigureAsync(command)); + + Assert.Contains("only supported on Linux", ex.Message); + } + } +#endif } }