예제 #1
0
        public async Task LoadGitExecutionInfo(AgentTaskPluginExecutionContext context, bool useBuiltInGit)
        {
            // Resolve the location of git.
            if (useBuiltInGit)
            {
#if OS_WINDOWS
                string agentHomeDir = context.Variables.GetValueOrDefault("agent.homedirectory")?.Value;
                ArgUtil.NotNullOrEmpty(agentHomeDir, nameof(agentHomeDir));
                gitPath = Path.Combine(agentHomeDir, "externals", "git", "cmd", $"git.exe");

                // Prepend the PATH.
                context.Output(StringUtil.Loc("Prepending0WithDirectoryContaining1", "Path", Path.GetFileName(gitPath)));
                context.PrependPath(Path.GetDirectoryName(gitPath));
                context.Debug($"PATH: '{Environment.GetEnvironmentVariable("PATH")}'");
#else
                // There is no built-in git for OSX/Linux
                gitPath = null;
#endif
            }
            else
            {
                gitPath = WhichUtil.Which("git", require: true, trace: context);
            }

            ArgUtil.File(gitPath, nameof(gitPath));

            // Get the Git version.
            gitVersion = await GitVersion(context);

            ArgUtil.NotNull(gitVersion, nameof(gitVersion));
            context.Debug($"Detect git version: {gitVersion.ToString()}.");

            // Resolve the location of git-lfs.
            // This should be best effort since checkout lfs objects is an option.
            // We will check and ensure git-lfs version later
            gitLfsPath = WhichUtil.Which("git-lfs", require: false, trace: context);

            // Get the Git-LFS version if git-lfs exist in %PATH%.
            if (!string.IsNullOrEmpty(gitLfsPath))
            {
                gitLfsVersion = await GitLfsVersion(context);

                context.Debug($"Detect git-lfs version: '{gitLfsVersion?.ToString() ?? string.Empty}'.");
            }

            // required 2.0, all git operation commandline args need min git version 2.0
            Version minRequiredGitVersion = new Version(2, 0);
            EnsureGitVersion(minRequiredGitVersion, throwOnNotMatch: true);

            // suggest user upgrade to 2.9 for better git experience
            Version recommendGitVersion = new Version(2, 9);
            if (!EnsureGitVersion(recommendGitVersion, throwOnNotMatch: false))
            {
                context.Output(StringUtil.Loc("UpgradeToLatestGit", recommendGitVersion, gitVersion));
            }

            // Set the user agent.
            gitHttpUserAgentEnv = $"git/{gitVersion.ToString()} (vsts-agent-git/{context.Variables.GetValueOrDefault("agent.version")?.Value ?? "unknown"})";
            context.Debug($"Set git useragent to: {gitHttpUserAgentEnv}.");
        }
        /// <summary>
        ///  Git package verification result using the "debsums" utility.
        /// </summary>
        /// <returns>String with the "debsums" output</returns>
        private async Task <string> GetPackageVerificationResult()
        {
            var debsums       = WhichUtil.Which("debsums");
            var stringBuilder = new StringBuilder();

            using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
            {
                processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs mes) =>
                {
                    stringBuilder.AppendLine(mes.Data);
                };
                processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs mes) =>
                {
                    stringBuilder.AppendLine(mes.Data);
                };

                await processInvoker.ExecuteAsync(
                    workingDirectory : HostContext.GetDirectory(WellKnownDirectory.Bin),
                    fileName : debsums,
                    arguments : string.Empty,
                    environment : null,
                    requireExitCodeZero : false,
                    outputEncoding : null,
                    killProcessOnCancel : false,
                    cancellationToken : default(CancellationToken)
                    );
            }

            return(stringBuilder.ToString());
        }
            public void Check(string name, string fileName = null, string[] filePaths = null)
            {
                ArgUtil.NotNullOrEmpty(name, nameof(name));
                _cancellationToken.ThrowIfCancellationRequested();

                try
                {
                    // Which the file.
                    string filePath = WhichUtil.Which(fileName ?? name, trace: _trace);
                    if (string.IsNullOrEmpty(filePath))
                    {
                        // Fallback to the well-known locations.
                        foreach (string candidateFilePath in filePaths ?? new string[0])
                        {
                            _trace.Info($"Checking file: '{candidateFilePath}'");
                            if (File.Exists(candidateFilePath))
                            {
                                filePath = candidateFilePath;
                                break;
                            }
                        }
                    }

                    if (!string.IsNullOrEmpty(filePath))
                    {
                        _trace.Info($"Adding '{name}': '{filePath}'");
                        _capabilities.Add(new Capability(name, filePath));
                    }
                }
                catch (Exception ex)
                {
                    _trace.Error(ex);
                }
            }
        private void CreateCredentialStoreFile()
        {
            File.WriteAllText(_credStoreFile, "");
            File.SetAttributes(_credStoreFile, File.GetAttributes(_credStoreFile) | FileAttributes.Hidden);

            // Try to lock down the .credentials_store file to the owner/group
            var chmodPath = WhichUtil.Which("chmod", trace: Trace);

            if (!String.IsNullOrEmpty(chmodPath))
            {
                var arguments = $"600 {new FileInfo(_credStoreFile).FullName}";
                using (var invoker = HostContext.CreateService <IProcessInvoker>())
                {
                    var exitCode = invoker.ExecuteAsync(HostContext.GetDirectory(WellKnownDirectory.Root), chmodPath, arguments, null, default(CancellationToken)).GetAwaiter().GetResult();
                    if (exitCode == 0)
                    {
                        Trace.Info("Successfully set permissions for credentials store file {0}", _credStoreFile);
                    }
                    else
                    {
                        Trace.Warning("Unable to successfully set permissions for credentials store file {0}. Received exit code {1} from {2}", _credStoreFile, exitCode, chmodPath);
                    }
                }
            }
            else
            {
                Trace.Warning("Unable to locate chmod to set permissions for credentials store file {0}.", _credStoreFile);
            }
        }
        public async Task ExecAsync(string workingDirectory, string toolName, string argLine)
        {
            Trace.Entering();

            string toolPath = WhichUtil.Which(toolName, trace: Trace);

            Trace.Info($"Running {toolPath} {argLine}");

            var processInvoker = HostContext.CreateService <IProcessInvoker>();

            processInvoker.OutputDataReceived += OnOutputDataReceived;
            processInvoker.ErrorDataReceived  += OnErrorDataReceived;

            try
            {
                using (var cs = new CancellationTokenSource(TimeSpan.FromSeconds(45)))
                {
                    await processInvoker.ExecuteAsync(workingDirectory, toolPath, argLine, null, true, cs.Token);
                }
            }
            finally
            {
                processInvoker.OutputDataReceived -= OnOutputDataReceived;
                processInvoker.ErrorDataReceived  -= OnErrorDataReceived;
            }
        }
예제 #6
0
        private void RestartBasedOnUserInput(CommandSettings command)
        {
            Trace.Info("Asking the user to restart the machine to launch agent and for autologon settings to take effect.");
            _terminal.WriteLine(StringUtil.Loc("RestartMessage"));

            var noRestart = command.GetNoRestart();

            if (!noRestart)
            {
                var shutdownExePath = WhichUtil.Which("shutdown.exe", trace: Trace);

                Trace.Info("Restarting the machine in 15 seconds");
                _terminal.WriteLine(StringUtil.Loc("RestartIn15SecMessage"));
                string msg = StringUtil.Loc("ShutdownMessage");
                //we are not using ProcessInvoker here as today it is not designed for 'fire and forget' pattern
                //ExecuteAsync API of ProcessInvoker waits for the process to exit
                var args = $@"-r -t 15 -c ""{msg}""";
                Trace.Info($"Shutdown.exe path: {shutdownExePath}. Arguments: {args}");
                Process.Start(shutdownExePath, $@"{args}");
            }
            else
            {
                _terminal.WriteLine(StringUtil.Loc("NoRestartSuggestion"));
            }
        }
예제 #7
0
        public async Task <bool> SelfUpdate(AgentRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveRunner, CancellationToken token)
        {
            Busy = true;
            try
            {
                if (!await UpdateNeeded(updateMessage.TargetVersion, token))
                {
                    Trace.Info($"Can't find available update package.");
                    return(false);
                }

                Trace.Info($"An update is available.");

                // Print console line that warn user not shutdown runner.
                await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner.");
                await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner");

                await DownloadLatestRunner(token);

                Trace.Info($"Download latest runner and unzip into runner root.");

                // wait till all running job finish
                await UpdateRunnerUpdateStateAsync("Waiting for current job finish running.");

                await jobDispatcher.WaitAsync(token);

                Trace.Info($"All running job has exited.");

                // delete runner backup
                DeletePreviousVersionRunnerBackup(token);
                Trace.Info($"Delete old version runner backup.");

                // generate update script from template
                await UpdateRunnerUpdateStateAsync("Generate and execute update script.");

                string updateScript = GenerateUpdateScript(restartInteractiveRunner);
                Trace.Info($"Generate update script into: {updateScript}");

                // kick off update script
                Process invokeScript = new Process();
#if OS_WINDOWS
                invokeScript.StartInfo.FileName  = WhichUtil.Which("cmd.exe", trace: Trace);
                invokeScript.StartInfo.Arguments = $"/c \"{updateScript}\"";
#elif (OS_OSX || OS_LINUX)
                invokeScript.StartInfo.FileName  = WhichUtil.Which("bash", trace: Trace);
                invokeScript.StartInfo.Arguments = $"\"{updateScript}\"";
#endif
                invokeScript.Start();
                Trace.Info($"Update script start running");

                await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should back online within 10 seconds.");

                return(true);
            }
            finally
            {
                Busy = false;
            }
        }
        public override void Initialize(IHostContext hostContext)
        {
            ArgUtil.NotNull(hostContext, nameof(hostContext));

            base.Initialize(hostContext);
            DockerPath          = WhichUtil.Which("docker", true, Trace);
            DockerInstanceLabel = IOUtil.GetPathHash(hostContext.GetDirectory(WellKnownDirectory.Root)).Substring(0, 6);
        }
예제 #9
0
        public async Task LoadGitExecutionInfo(IExecutionContext context, bool useBuiltInGit)
        {
            // Resolve the location of git.
            if (useBuiltInGit)
            {
#if OS_WINDOWS
                _gitPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "git", "cmd", $"git{IOUtil.ExeExtension}");

                // Prepend the PATH.
                context.Output(StringUtil.Loc("Prepending0WithDirectoryContaining1", Constants.PathVariable, Path.GetFileName(_gitPath)));
                PathUtil.PrependPath(Path.GetDirectoryName(_gitPath));
                context.Debug($"{Constants.PathVariable}: '{Environment.GetEnvironmentVariable(Constants.PathVariable)}'");
#else
                // There is no built-in git for OSX/Linux
                _gitPath = null;
#endif
            }
            else
            {
                _gitPath = WhichUtil.Which("git", require: true, trace: Trace);
            }

            ArgUtil.File(_gitPath, nameof(_gitPath));

            // Get the Git version.
            _gitVersion = await GitVersion(context);

            ArgUtil.NotNull(_gitVersion, nameof(_gitVersion));
            context.Debug($"Detect git version: {_gitVersion.ToString()}.");

            // Resolve the location of git-lfs.
            // This should be best effort since checkout lfs objects is an option.
            // We will check and ensure git-lfs version later
            _gitLfsPath = WhichUtil.Which("git-lfs", require: false, trace: Trace);

            // Get the Git-LFS version if git-lfs exist in %PATH%.
            if (!string.IsNullOrEmpty(_gitLfsPath))
            {
                _gitLfsVersion = await GitLfsVersion(context);

                context.Debug($"Detect git-lfs version: '{_gitLfsVersion?.ToString() ?? string.Empty}'.");
            }

            // required 2.0, all git operation commandline args need min git version 2.0
            Version minRequiredGitVersion = new Version(2, 0);
            EnsureGitVersion(minRequiredGitVersion, throwOnNotMatch: true);

            // suggest user upgrade to 2.9 for better git experience
            Version recommendGitVersion = new Version(2, 9);
            if (!EnsureGitVersion(recommendGitVersion, throwOnNotMatch: false))
            {
                context.Output(StringUtil.Loc("UpgradeToLatestGit", recommendGitVersion, _gitVersion));
            }

            // Set the user agent.
            _gitHttpUserAgentEnv = $"git/{_gitVersion.ToString()} (vsts-agent-git/{BuildConstants.AgentPackage.Version})";
            context.Debug($"Set git useragent to: {_gitHttpUserAgentEnv}.");
        }
예제 #10
0
        public async Task <bool> SelfUpdate(AgentRefreshMessage updateMessage, IJobDispatcher jobDispatcher, bool restartInteractiveAgent, CancellationToken token)
        {
            ArgUtil.NotNull(updateMessage, nameof(updateMessage));
            ArgUtil.NotNull(jobDispatcher, nameof(jobDispatcher));
            if (!await UpdateNeeded(updateMessage.TargetVersion, token))
            {
                Trace.Info($"Can't find available update package.");
                return(false);
            }

            Trace.Info($"An update is available.");

            // Print console line that warn user not shutdown agent.
            await UpdateAgentUpdateStateAsync(StringUtil.Loc("UpdateInProgress"));
            await UpdateAgentUpdateStateAsync(StringUtil.Loc("DownloadAgent", _targetPackage.Version));

            await DownloadLatestAgent(token);

            Trace.Info($"Download latest agent and unzip into agent root.");

            // wait till all running job finish
            await UpdateAgentUpdateStateAsync(StringUtil.Loc("EnsureJobFinished"));

            await jobDispatcher.WaitAsync(token);

            Trace.Info($"All running jobs have exited.");

            // delete agent backup
            DeletePreviousVersionAgentBackup(token);
            Trace.Info($"Delete old version agent backup.");

            // generate update script from template
            await UpdateAgentUpdateStateAsync(StringUtil.Loc("GenerateAndRunUpdateScript"));

            string updateScript = GenerateUpdateScript(restartInteractiveAgent);

            Trace.Info($"Generate update script into: {updateScript}");

            // kick off update script
            Process invokeScript = new Process();

            if (PlatformUtil.RunningOnWindows)
            {
                invokeScript.StartInfo.FileName  = WhichUtil.Which("cmd.exe", trace: Trace);
                invokeScript.StartInfo.Arguments = $"/c \"{updateScript}\"";
            }
            else
            {
                invokeScript.StartInfo.FileName  = WhichUtil.Which("bash", trace: Trace);
                invokeScript.StartInfo.Arguments = $"\"{updateScript}\"";
            }
            invokeScript.Start();
            Trace.Info($"Update script start running");

            await UpdateAgentUpdateStateAsync(StringUtil.Loc("AgentExit"));

            return(true);
        }
예제 #11
0
        private async Task ExtractRunnerPackage(string archiveFile, string extractDirectory, CancellationToken token)
        {
            var stopWatch = Stopwatch.StartNew();

            if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
            {
                ZipFile.ExtractToDirectory(archiveFile, extractDirectory);
            }
            else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
            {
                string tar = WhichUtil.Which("tar", trace: Trace);

                if (string.IsNullOrEmpty(tar))
                {
                    throw new NotSupportedException($"tar -xzf");
                }

                // tar -xzf
                using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
                {
                    processInvoker.OutputDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) =>
                    {
                        if (!string.IsNullOrEmpty(args.Data))
                        {
                            Trace.Info(args.Data);
                        }
                    });

                    processInvoker.ErrorDataReceived += new EventHandler <ProcessDataReceivedEventArgs>((sender, args) =>
                    {
                        if (!string.IsNullOrEmpty(args.Data))
                        {
                            Trace.Error(args.Data);
                        }
                    });

                    int exitCode = await processInvoker.ExecuteAsync(extractDirectory, tar, $"-xzf \"{archiveFile}\"", null, token);

                    if (exitCode != 0)
                    {
                        throw new NotSupportedException($"Can't use 'tar -xzf' extract archive file: {archiveFile}. return code: {exitCode}.");
                    }
                }
            }
            else
            {
                throw new NotSupportedException($"{archiveFile}");
            }

            stopWatch.Stop();
            Trace.Info($"Finished getting latest runner package at: {extractDirectory}.");
            _updateTrace.Add($"PackageExtractTime: {stopWatch.ElapsedMilliseconds}ms");
        }
        /// <summary>
        /// Initializes svn command path and execution environment
        /// </summary>
        /// <param name="context">The build commands' execution context</param>
        /// <param name="endpoint">The Subversion server endpoint providing URL, username/password, and untrasted certs acceptace information</param>
        /// <param name="cancellationToken">The cancellation token used to stop svn command execution</param>
        public void Init(
            AgentTaskPluginExecutionContext context,
            Pipelines.RepositoryResource repository,
            CancellationToken cancellationToken)
        {
            // Validation.
            ArgUtil.NotNull(context, nameof(context));
            ArgUtil.NotNull(repository, nameof(repository));
            ArgUtil.NotNull(cancellationToken, nameof(cancellationToken));

            ArgUtil.NotNull(repository.Url, nameof(repository.Url));
            ArgUtil.Equal(true, repository.Url.IsAbsoluteUri, nameof(repository.Url.IsAbsoluteUri));

            ArgUtil.NotNull(repository.Endpoint, nameof(repository.Endpoint));
            ServiceEndpoint endpoint = context.Endpoints.Single(
                x => (repository.Endpoint.Id != Guid.Empty && x.Id == repository.Endpoint.Id) ||
                (repository.Endpoint.Id == Guid.Empty && string.Equals(x.Name, repository.Endpoint.Name.ToString(), StringComparison.OrdinalIgnoreCase)));

            ArgUtil.NotNull(endpoint.Data, nameof(endpoint.Data));
            ArgUtil.NotNull(endpoint.Authorization, nameof(endpoint.Authorization));
            ArgUtil.NotNull(endpoint.Authorization.Parameters, nameof(endpoint.Authorization.Parameters));
            ArgUtil.Equal(EndpointAuthorizationSchemes.UsernamePassword, endpoint.Authorization.Scheme, nameof(endpoint.Authorization.Scheme));

            _context           = context;
            _repository        = repository;
            _cancellationToken = cancellationToken;

            // Find svn in %Path%
            string svnPath = WhichUtil.Which("svn", trace: context);

            if (string.IsNullOrEmpty(svnPath))
            {
                throw new Exception(StringUtil.Loc("SvnNotInstalled"));
            }
            else
            {
                _context.Debug($"Found svn installation path: {svnPath}.");
                _svn = svnPath;
            }

            // External providers may need basic auth or tokens
            endpoint.Authorization.Parameters.TryGetValue(EndpointAuthorizationParameters.Username, out _username);
            endpoint.Authorization.Parameters.TryGetValue(EndpointAuthorizationParameters.Password, out _password);

            if (endpoint.Data.TryGetValue(EndpointData.AcceptUntrustedCertificates, out string endpointAcceptUntrustedCerts))
            {
                _acceptUntrusted = StringUtil.ConvertToBoolean(endpointAcceptUntrustedCerts);
            }

            _acceptUntrusted = _acceptUntrusted || (context.GetCertConfiguration()?.SkipServerCertificateValidation ?? false);
        }
        public void SetupClientCertificate(string clientCert, string clientCertKey, string clientCertArchive, string clientCertPassword)
        {
            ExecutionContext.Debug("Convert client certificate from 'pkcs' format to 'jks' format.");
            string toolPath = WhichUtil.Which("keytool", true, ExecutionContext);
            string jksFile  = Path.Combine(ExecutionContext.Variables.GetValueOrDefault("agent.tempdirectory")?.Value, $"{Guid.NewGuid()}.jks");
            string argLine;

            if (!string.IsNullOrEmpty(clientCertPassword))
            {
                argLine = $"-importkeystore -srckeystore \"{clientCertArchive}\" -srcstoretype pkcs12 -destkeystore \"{jksFile}\" -deststoretype JKS -srcstorepass \"{clientCertPassword}\" -deststorepass \"{clientCertPassword}\"";
            }
            else
            {
                argLine = $"-importkeystore -srckeystore \"{clientCertArchive}\" -srcstoretype pkcs12 -destkeystore \"{jksFile}\" -deststoretype JKS";
            }

            ExecutionContext.Command($"{toolPath} {argLine}");

            using (var processInvoker = new ProcessInvoker(ExecutionContext))
            {
                processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs args) =>
                {
                    if (!string.IsNullOrEmpty(args.Data))
                    {
                        ExecutionContext.Output(args.Data);
                    }
                };
                processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs args) =>
                {
                    if (!string.IsNullOrEmpty(args.Data))
                    {
                        ExecutionContext.Output(args.Data);
                    }
                };

                processInvoker.ExecuteAsync(ExecutionContext.Variables.GetValueOrDefault("system.defaultworkingdirectory")?.Value, toolPath, argLine, null, true, CancellationToken.None).GetAwaiter().GetResult();

                if (!string.IsNullOrEmpty(clientCertPassword))
                {
                    ExecutionContext.Debug($"Set TF_ADDITIONAL_JAVA_ARGS=-Djavax.net.ssl.keyStore={jksFile} -Djavax.net.ssl.keyStorePassword={clientCertPassword}");
                    AdditionalEnvironmentVariables["TF_ADDITIONAL_JAVA_ARGS"] = $"-Djavax.net.ssl.keyStore={jksFile} -Djavax.net.ssl.keyStorePassword={clientCertPassword}";
                }
                else
                {
                    ExecutionContext.Debug($"Set TF_ADDITIONAL_JAVA_ARGS=-Djavax.net.ssl.keyStore={jksFile}");
                    AdditionalEnvironmentVariables["TF_ADDITIONAL_JAVA_ARGS"] = $"-Djavax.net.ssl.keyStore={jksFile}";
                }
            }
        }
예제 #14
0
        public Task GetSourceAsync(
            IExecutionContext executionContext,
            ServiceEndpoint endpoint,
            CancellationToken cancellationToken)
        {
            Trace.Entering();
            ArgUtil.Equal(RunMode.Local, HostContext.RunMode, nameof(HostContext.RunMode));
            ArgUtil.Equal(HostTypes.Build, executionContext.Variables.System_HostType, nameof(executionContext.Variables.System_HostType));
            ArgUtil.NotNull(executionContext, nameof(executionContext));
            ArgUtil.NotNull(endpoint, nameof(endpoint));

            bool preferGitFromPath;

#if OS_WINDOWS
            preferGitFromPath = false;
#else
            preferGitFromPath = true;
#endif
            if (!preferGitFromPath)
            {
                // Add git to the PATH.
                string gitPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "git", "cmd", $"git{IOUtil.ExeExtension}");
                ArgUtil.File(gitPath, nameof(gitPath));
                executionContext.Output(StringUtil.Loc("Prepending0WithDirectoryContaining1", Constants.PathVariable, Path.GetFileName(gitPath)));
                PathUtil.PrependPath(Path.GetDirectoryName(gitPath));
                executionContext.Debug($"{Constants.PathVariable}: '{Environment.GetEnvironmentVariable(Constants.PathVariable)}'");
            }
            else
            {
                // Validate git is in the PATH.
                WhichUtil.Which("git", require: true, trace: Trace);
            }

            // Override build.sourcesDirectory.
            //
            // Technically the value will be out of sync with the tracking file. The tracking file
            // is created during job initialization (Get Sources is later). That is OK, since the
            // local-run-sources-directory should not participate in cleanup anyway.
            string localDirectory = endpoint.Data?["localDirectory"];
            ArgUtil.Directory(localDirectory, nameof(localDirectory));
            ArgUtil.Directory(Path.Combine(localDirectory, ".git"), "localDotGitDirectory");
            executionContext.Variables.Set(Constants.Variables.System.DefaultWorkingDirectory, localDirectory);
            executionContext.Variables.Set(Constants.Variables.Build.SourcesDirectory, localDirectory);
            executionContext.Variables.Set(Constants.Variables.Build.RepoLocalPath, localDirectory);

            // todo: consider support for clean

            return(Task.CompletedTask);
        }
예제 #15
0
        public void WhichHandleFullyQualifiedPath()
        {
            using (TestHostContext hc = new TestHostContext(this))
            {
                //Arrange
                Tracing trace = hc.GetTrace();

                // Act.
                var gitPath  = WhichUtil.Which("git", require: true, trace: trace);
                var gitPath2 = WhichUtil.Which(gitPath, require: true, trace: trace);

                // Assert.
                Assert.Equal(gitPath, gitPath2);
            }
        }
        public async Task <Boolean> VerifyAsync(Definition definition, CancellationToken token)
        {
            ArgUtil.NotNull(definition, nameof(definition));

            // This is used for the Checkout task.
            // We can consider it verified since it's embedded in the Agent code.
            if (String.IsNullOrEmpty(definition.ZipPath))
            {
                return(true);
            }

            // Find NuGet
            String nugetPath = WhichUtil.Which("nuget", require: true);

            var           configurationStore = HostContext.GetService <IConfigurationStore>();
            AgentSettings settings           = configurationStore.GetSettings();
            String        fingerprint        = settings.Fingerprint;
            String        taskZipPath        = definition.ZipPath;
            String        taskNugetPath      = definition.ZipPath.Replace(".zip", ".nupkg");

            // Rename .zip to .nupkg
            File.Move(taskZipPath, taskNugetPath);

            String arguments = $"verify -Signatures \"{taskNugetPath}\" -CertificateFingerprint {fingerprint} -Verbosity Detailed";

            // Run nuget verify
            using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
            {
                int exitCode = await processInvoker.ExecuteAsync(workingDirectory : HostContext.GetDirectory(WellKnownDirectory.Root),
                                                                 fileName : nugetPath,
                                                                 arguments : arguments,
                                                                 environment : null,
                                                                 requireExitCodeZero : false,
                                                                 outputEncoding : null,
                                                                 killProcessOnCancel : false,
                                                                 cancellationToken : token);

                // Rename back to zip
                File.Move(taskNugetPath, taskZipPath);

                if (exitCode != 0)
                {
                    return(false);
                }
            }

            return(true);
        }
예제 #17
0
        public void WhichReturnsNullWhenNotFound()
        {
            using (TestHostContext hc = new TestHostContext(this))
            {
                //Arrange
                Tracing trace = hc.GetTrace();

                // Act.
                string nosuch = WhichUtil.Which("no-such-file-cf7e351f", trace: trace);

                trace.Info($"result: {nosuch ?? string.Empty}");

                // Assert.
                Assert.True(string.IsNullOrEmpty(nosuch), "Path should not be resolved");
            }
        }
예제 #18
0
        private async Task CreateRepository(TestHostContext hostConetxt, string path, string url)
        {
            Directory.CreateDirectory(path);
            var gitPath     = WhichUtil.Which("git", true);
            var environment = new Dictionary <string, string>();

            using (var processInvoker = new ProcessInvoker(hostConetxt.GetTrace()))
            {
                await processInvoker.ExecuteAsync(path, gitPath, "init", environment, CancellationToken.None);
            }

            using (var processInvoker = new ProcessInvoker(hostConetxt.GetTrace()))
            {
                await processInvoker.ExecuteAsync(path, gitPath, $"remote add origin {url}", environment, CancellationToken.None);
            }
        }
예제 #19
0
        public void UseWhichFindGit()
        {
            using (TestHostContext hc = new TestHostContext(this))
            {
                //Arrange
                Tracing trace = hc.GetTrace();

                // Act.
                string gitPath = WhichUtil.Which("git", trace: trace);

                trace.Info($"Which(\"git\") returns: {gitPath ?? string.Empty}");

                // Assert.
                Assert.True(!string.IsNullOrEmpty(gitPath) && File.Exists(gitPath), $"Unable to find Git through: {nameof(WhichUtil.Which)}");
            }
        }
            public async Task CheckToolOutputAsync(string name, string fileName, string arguments)
            {
                _trace.Entering();
                ArgUtil.NotNullOrEmpty(name, nameof(name));
                ArgUtil.NotNullOrEmpty(fileName, nameof(fileName));
                try
                {
                    // Attempt to locate the tool.
                    string filePath = WhichUtil.Which(fileName, trace: _trace);
                    if (string.IsNullOrEmpty(filePath))
                    {
                        return;
                    }

                    // Invoke the tool and capture the output.
                    var output = new StringBuilder();
                    using (var processInvoker = _hostContext.CreateService <IProcessInvoker>())
                    {
                        processInvoker.OutputDataReceived +=
                            (object sender, ProcessDataReceivedEventArgs args) =>
                        {
                            if (!string.IsNullOrEmpty(args.Data))
                            {
                                output.Append(args.Data);
                            }
                        };
                        await processInvoker.ExecuteAsync(
                            workingDirectory : string.Empty,
                            fileName : filePath,
                            arguments : arguments ?? string.Empty,
                            environment : null,
                            cancellationToken : _cancellationToken);
                    }

                    // Add the capability.
                    if (output.Length > 0)
                    {
                        string value = output.ToString();
                        _trace.Info($"Adding '{name}': '{value}'");
                        _capabilities.Add(new Capability(name, value));
                    }
                }
                catch (Exception ex) when(!(ex is OperationCanceledException))
                {
                    _trace.Error(ex);
                }
            }
예제 #21
0
        public RSACryptoServiceProvider CreateKey()
        {
            RSACryptoServiceProvider rsa = null;

            if (!File.Exists(_keyFile))
            {
                Trace.Info("Creating new RSA key using 2048-bit key length");

                rsa = new RSACryptoServiceProvider(2048);

                // Now write the parameters to disk
                IOUtil.SaveObject(new RSAParametersSerializable(rsa.ExportParameters(true)), _keyFile);
                Trace.Info("Successfully saved RSA key parameters to file {0}", _keyFile);

                // Try to lock down the credentials_key file to the owner/group
                var chmodPath = WhichUtil.Which("chmod", trace: Trace);
                if (!String.IsNullOrEmpty(chmodPath))
                {
                    var arguments = $"600 {new FileInfo(_keyFile).FullName}";
                    using (var invoker = _context.CreateService <IProcessInvoker>())
                    {
                        var exitCode = invoker.ExecuteAsync(HostContext.GetDirectory(WellKnownDirectory.Root), chmodPath, arguments, null, default(CancellationToken)).GetAwaiter().GetResult();
                        if (exitCode == 0)
                        {
                            Trace.Info("Successfully set permissions for RSA key parameters file {0}", _keyFile);
                        }
                        else
                        {
                            Trace.Warning("Unable to succesfully set permissions for RSA key parameters file {0}. Received exit code {1} from {2}", _keyFile, exitCode, chmodPath);
                        }
                    }
                }
                else
                {
                    Trace.Warning("Unable to locate chmod to set permissions for RSA key parameters file {0}.", _keyFile);
                }
            }
            else
            {
                Trace.Info("Found existing RSA key parameters file {0}", _keyFile);

                rsa = new RSACryptoServiceProvider();
                rsa.ImportParameters(IOUtil.LoadObject <RSAParametersSerializable>(_keyFile).RSAParameters);
            }

            return(rsa);
        }
예제 #22
0
        public async Task LoadGitExecutionInfo(RunnerActionPluginExecutionContext context)
        {
            // Resolve the location of git.
            gitPath = WhichUtil.Which("git", require: true, trace: context);
            ArgUtil.File(gitPath, nameof(gitPath));

            // Get the Git version.
            gitVersion = await GitVersion(context);

            ArgUtil.NotNull(gitVersion, nameof(gitVersion));
            context.Debug($"Detect git version: {gitVersion.ToString()}.");

            // Resolve the location of git-lfs.
            // This should be best effort since checkout lfs objects is an option.
            // We will check and ensure git-lfs version later
            gitLfsPath = WhichUtil.Which("git-lfs", require: false, trace: context);

            // Get the Git-LFS version if git-lfs exist in %PATH%.
            if (!string.IsNullOrEmpty(gitLfsPath))
            {
                gitLfsVersion = await GitLfsVersion(context);

                context.Debug($"Detect git-lfs version: '{gitLfsVersion?.ToString() ?? string.Empty}'.");
            }

            // required 2.0, all git operation commandline args need min git version 2.0
            Version minRequiredGitVersion = new Version(2, 0);

            EnsureGitVersion(minRequiredGitVersion, throwOnNotMatch: true);

            // suggest user upgrade to 2.9 for better git experience
            Version recommendGitVersion = new Version(2, 9);

            if (!EnsureGitVersion(recommendGitVersion, throwOnNotMatch: false))
            {
                context.Output($"To get a better Git experience, upgrade your Git to at least version '{recommendGitVersion}'. Your current Git version is '{gitVersion}'.");
            }

            // Set the user agent.
            string gitHttpUserAgentEnv = $"git/{gitVersion.ToString()} (github-actions-runner-git/{BuildConstants.RunnerPackage.Version})";

            context.Debug($"Set git useragent to: {gitHttpUserAgentEnv}.");
            gitEnv["GIT_HTTP_USER_AGENT"] = gitHttpUserAgentEnv;
        }
        /// <summary>
        /// Dumping cloud-init logs to diag folder of agent if cloud-init is installed on current machine.
        /// </summary>
        /// <param name="logsFile">Path to collect cloud-init logs</param>
        /// <returns>Returns the method execution logs</returns>
        private async Task <string> DumpCloudInitLogs(string logsFile)
        {
            var    builder   = new StringBuilder();
            string cloudInit = WhichUtil.Which("cloud-init", trace: Trace);

            if (string.IsNullOrEmpty(cloudInit))
            {
                return("Cloud-init isn't found on current machine.");
            }

            string arguments = $"collect-logs -t \"{logsFile}\"";

            try
            {
                using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
                {
                    processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs args) =>
                    {
                        builder.AppendLine(args.Data);
                    };

                    processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs args) =>
                    {
                        builder.AppendLine(args.Data);
                    };

                    await processInvoker.ExecuteAsync(
                        workingDirectory : HostContext.GetDirectory(WellKnownDirectory.Bin),
                        fileName : cloudInit,
                        arguments : arguments,
                        environment : null,
                        requireExitCodeZero : false,
                        outputEncoding : null,
                        killProcessOnCancel : false,
                        cancellationToken : default(CancellationToken));
                }
            }
            catch (Exception ex)
            {
                builder.AppendLine(ex.Message);
            }
            return(builder.ToString());
        }
예제 #24
0
        public void WhichThrowsWhenRequireAndNotFound()
        {
            using (TestHostContext hc = new TestHostContext(this))
            {
                //Arrange
                Tracing trace = hc.GetTrace();

                // Act.
                try
                {
                    WhichUtil.Which("no-such-file-cf7e351f", require: true, trace: trace);
                    throw new Exception("which should have thrown");
                }
                catch (FileNotFoundException ex)
                {
                    Assert.Equal("no-such-file-cf7e351f", ex.FileName);
                }
            }
        }
예제 #25
0
        public void Init(
            IExecutionContext context,
            ServiceEndpoint endpoint,
            CancellationToken cancellationToken)
        {
            // Validation.
            ArgUtil.NotNull(context, nameof(context));
            ArgUtil.NotNull(endpoint, nameof(endpoint));
            ArgUtil.NotNull(cancellationToken, nameof(cancellationToken));

            ArgUtil.NotNull(endpoint.Url, nameof(endpoint.Url));
            ArgUtil.Equal(true, endpoint.Url.IsAbsoluteUri, nameof(endpoint.Url.IsAbsoluteUri));
            ArgUtil.NotNull(endpoint.Data, nameof(endpoint.Data));

            ArgUtil.NotNull(endpoint.Authorization, nameof(endpoint.Authorization));
            ArgUtil.NotNull(endpoint.Authorization.Parameters, nameof(endpoint.Authorization.Parameters));
            ArgUtil.Equal(EndpointAuthorizationSchemes.UsernamePassword, endpoint.Authorization.Scheme, nameof(endpoint.Authorization.Scheme));

            _context           = context;
            _endpoint          = endpoint;
            _cancellationToken = cancellationToken;

            // Find svn in %Path%
            string svnPath = WhichUtil.Which("svn", trace: Trace);

            if (string.IsNullOrEmpty(svnPath))
            {
                throw new Exception(StringUtil.Loc("SvnNotInstalled"));
            }
            else
            {
                _context.Debug($"Found svn installation path: {svnPath}.");
                _svn = svnPath;
            }

            // External providers may need basic auth or tokens
            endpoint.Authorization.Parameters.TryGetValue(EndpointAuthorizationParameters.Username, out _username);
            endpoint.Authorization.Parameters.TryGetValue(EndpointAuthorizationParameters.Password, out _password);

            _acceptUntrusted = endpoint.Data.ContainsKey(EndpointData.SvnAcceptUntrustedCertificates) &&
                               StringUtil.ConvertToBoolean(endpoint.Data[EndpointData.SvnAcceptUntrustedCertificates], defaultValue: false);
        }
예제 #26
0
        private async Task SwitchToUtf8Codepage(IStep step)
        {
            if (!PlatformUtil.RunningOnWindows)
            {
                return;
            }

            try
            {
                if (step.ExecutionContext.Variables.Retain_Default_Encoding != true && Console.InputEncoding.CodePage != 65001)
                {
                    using (var p = HostContext.CreateService <IProcessInvoker>())
                    {
                        // Use UTF8 code page
                        int exitCode = await p.ExecuteAsync(workingDirectory : HostContext.GetDirectory(WellKnownDirectory.Work),
                                                            fileName : WhichUtil.Which("chcp", true, Trace),
                                                            arguments : "65001",
                                                            environment : null,
                                                            requireExitCodeZero : false,
                                                            outputEncoding : null,
                                                            killProcessOnCancel : false,
                                                            redirectStandardIn : null,
                                                            inheritConsoleHandler : true,
                                                            cancellationToken : step.ExecutionContext.CancellationToken);

                        if (exitCode == 0)
                        {
                            Trace.Info("Successfully returned to code page 65001 (UTF8)");
                        }
                        else
                        {
                            Trace.Warning($"'chcp 65001' failed with exit code {exitCode}");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.Warning($"'chcp 65001' failed with exception {ex.Message}");
            }
        }
예제 #27
0
        private async Task ConfigurePowerOptions()
        {
            var filePath = WhichUtil.Which("powercfg.exe", require: true, trace: Trace);

            string[] commands = new string[] { "/Change monitor-timeout-ac 0", "/Change monitor-timeout-dc 0" };

            foreach (var command in commands)
            {
                try
                {
                    Trace.Info($"Running powercfg.exe with {command}");
                    using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
                    {
                        processInvoker.OutputDataReceived += delegate(object sender, ProcessDataReceivedEventArgs message)
                        {
                            Trace.Info(message.Data);
                        };

                        processInvoker.ErrorDataReceived += delegate(object sender, ProcessDataReceivedEventArgs message)
                        {
                            Trace.Error(message.Data);
                            _terminal.WriteError(message.Data);
                        };

                        await processInvoker.ExecuteAsync(
                            workingDirectory : string.Empty,
                            fileName : filePath,
                            arguments : command,
                            environment : null,
                            cancellationToken : CancellationToken.None);
                    }
                }
                catch (Exception ex)
                {
                    //we will not stop the configuration. just show the warning and continue
                    _terminal.WriteError(StringUtil.Loc("PowerOptionsConfigError"));
                    Trace.Error(ex);
                }
            }
        }
예제 #28
0
        public static async Task SetEncoding(IHostContext hostContext, Tracing trace, CancellationToken cancellationToken)
        {
#if OS_WINDOWS
            try
            {
                if (Console.InputEncoding.CodePage != 65001)
                {
                    using (var p = hostContext.CreateService <IProcessInvoker>())
                    {
                        // Use UTF8 code page
                        int exitCode = await p.ExecuteAsync(workingDirectory : hostContext.GetDirectory(WellKnownDirectory.Work),
                                                            fileName : WhichUtil.Which("chcp", true, trace),
                                                            arguments : "65001",
                                                            environment : null,
                                                            requireExitCodeZero : false,
                                                            outputEncoding : null,
                                                            killProcessOnCancel : false,
                                                            redirectStandardIn : null,
                                                            inheritConsoleHandler : true,
                                                            cancellationToken : cancellationToken);

                        if (exitCode == 0)
                        {
                            trace.Info("Successfully returned to code page 65001 (UTF8)");
                        }
                        else
                        {
                            trace.Warning($"'chcp 65001' failed with exit code {exitCode}");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                trace.Warning($"'chcp 65001' failed with exception {ex.Message}");
            }
#endif
            // Dummy variable to prevent compiler error CS1998: "This async method lacks 'await' operators and will run synchronously..."
            await Task.CompletedTask;
        }
        /// <summary>
        ///  Get user groups on a non-windows platform using core utility "id".
        /// </summary>
        /// <returns>Returns the string with user groups</returns>
        private async Task <string> GetUserGroupsOnNonWindows()
        {
            var idUtil        = WhichUtil.Which("id");
            var stringBuilder = new StringBuilder();

            try
            {
                using (var processInvoker = HostContext.CreateService <IProcessInvoker>())
                {
                    processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs mes) =>
                    {
                        stringBuilder.AppendLine(mes.Data);
                    };
                    processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs mes) =>
                    {
                        stringBuilder.AppendLine(mes.Data);
                    };

                    await processInvoker.ExecuteAsync(
                        workingDirectory : HostContext.GetDirectory(WellKnownDirectory.Bin),
                        fileName : idUtil,
                        arguments : "-nG",
                        environment : null,
                        requireExitCodeZero : false,
                        outputEncoding : null,
                        killProcessOnCancel : false,
                        cancellationToken : default(CancellationToken)
                        );
                }
            }
            catch (Exception ex)
            {
                stringBuilder.AppendLine(ex.Message);
            }

            return(stringBuilder.ToString());
        }
예제 #30
0
        private async Task <string> GitAsync(string arguments, CancellationToken token)
        {
            // Resolve the location of git.
            if (_gitPath == null)
            {
#if OS_WINDOWS
                _gitPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), "git", "cmd", $"git{IOUtil.ExeExtension}");
                ArgUtil.File(_gitPath, nameof(_gitPath));
#else
                _gitPath = WhichUtil.Which("git", require: true);
#endif
            }

            // Prepare the environment variables to overlay.
            var overlayEnvironment = new Dictionary <string, string>(StringComparer.Ordinal);
            overlayEnvironment["GIT_TERMINAL_PROMPT"] = "0";
            // Skip any GIT_TRACE variable since GIT_TRACE will affect ouput from every git command.
            // This will fail the parse logic for detect git version, remote url, etc.
            // Ex.
            //      SET GIT_TRACE=true
            //      git version
            //      11:39:58.295959 git.c:371               trace: built-in: git 'version'
            //      git version 2.11.1.windows.1
            IDictionary currentEnvironment = Environment.GetEnvironmentVariables();
            foreach (DictionaryEntry entry in currentEnvironment)
            {
                string key = entry.Key as string ?? string.Empty;
                if (string.Equals(key, "GIT_TRACE", StringComparison.OrdinalIgnoreCase) ||
                    key.StartsWith("GIT_TRACE_", StringComparison.OrdinalIgnoreCase))
                {
                    overlayEnvironment[key] = string.Empty;
                }
            }

            // Run git and return the output from the streams.
            var output         = new StringBuilder();
            var processInvoker = HostContext.CreateService <IProcessInvoker>();
            Console.WriteLine();
            Console.WriteLine($"git {arguments}");
            processInvoker.OutputDataReceived += delegate(object sender, ProcessDataReceivedEventArgs message)
            {
                output.AppendLine(message.Data);
                Console.WriteLine(message.Data);
            };
            processInvoker.ErrorDataReceived += delegate(object sender, ProcessDataReceivedEventArgs message)
            {
                output.AppendLine(message.Data);
                Console.WriteLine(message.Data);
            };
#if OS_WINDOWS
            Encoding encoding = Encoding.UTF8;
#else
            Encoding encoding = null;
#endif
            await processInvoker.ExecuteAsync(
                workingDirectory : Directory.GetCurrentDirectory(),
                fileName : _gitPath,
                arguments : arguments,
                environment : overlayEnvironment,
                requireExitCodeZero : true,
                outputEncoding : encoding,
                cancellationToken : token);

            string result = output.ToString().Trim();
            ArgUtil.NotNullOrEmpty(result, nameof(result));
            return(result);
        }