/// <summary> /// Normalize line endings of content. /// </summary> /// <param name="filePath">Path of file</param> /// <param name="content">Content to normalize</param> /// <returns>Normalized content</returns> /// <remarks> /// Normalize based on the following rules: /// - Auto CRLF is assumed. /// - Check the git attributes the file to determine whether it has a specific setting for the file. If so, use that. /// - If no setting, or if auto, then determine whether incoming content differs in line ends vs. the /// OS setting, and replace if needed. /// </remarks> private string NormalizeLineEndings(string filePath, string content) { const string crlf = "\r\n"; const string lf = "\n"; // Check gitAttributes to determine whether the file has eof handling set. string eofAttr = LocalHelpers.ExecuteCommand(_gitExecutable, $"check-attr eol -- {filePath}", _logger); if (string.IsNullOrEmpty(eofAttr) || eofAttr.Contains("eol: unspecified") || eofAttr.Contains("eol: auto")) { if (Environment.NewLine != crlf) { return(content.Replace(crlf, Environment.NewLine)); } else if (Environment.NewLine == crlf && !content.Contains(crlf)) { return(content.Replace(lf, Environment.NewLine)); } } else if (eofAttr.Contains("eol: crlf")) { // Test to avoid adding extra \r. if (!content.Contains(crlf)) { return(content.Replace(lf, crlf)); } } else if (eofAttr.Contains("eol: lf")) { return(content.Replace(crlf, lf)); } else { throw new DarcException($"Unknown eof setting '{eofAttr}' for file '{filePath};"); } return(content); }
/// <summary> /// Cloning big repos takes a considerable amount of time when checking out the files. When /// working on batched subscription, the operation could take more than an hour causing the /// GitHub token to expire. By doing sparse and shallow checkout, we only deal with the files /// we need avoiding to check the complete repo shaving time from the overall push process /// </summary> /// <param name="filesToCommit">Collection of files to update.</param> /// <param name="repoUri">The repository to push the files to.</param> /// <param name="branch">The branch to push the files to.</param> /// <param name="commitMessage">The commmit message.</param> /// <returns></returns> protected async Task CommitFilesAsync( List <GitFile> filesToCommit, string repoUri, string branch, string commitMessage, ILogger logger, string pat, string dotnetMaestroName, string dotnetMaestroEmail) { logger.LogInformation("Pushing files to {branch}", branch); string tempRepoFolder = Path.Combine(TemporaryRepositoryPath, Path.GetRandomFileName()); string remote = "origin"; try { string clonedRepo = null; logger.LogInformation("Sparse and shallow checkout of branch {branch} in {repoUri}...", branch, repoUri); clonedRepo = LocalHelpers.SparseAndShallowCheckout(GitExecutable, repoUri, branch, tempRepoFolder, logger, remote, dotnetMaestroName, dotnetMaestroEmail, pat); foreach (GitFile file in filesToCommit) { string filePath = Path.Combine(clonedRepo, file.FilePath); if (file.Operation == GitFileOperation.Add) { if (!File.Exists(filePath)) { string parentFolder = Directory.GetParent(filePath).FullName; Directory.CreateDirectory(parentFolder); } using (FileStream stream = File.Create(filePath)) { byte[] contentBytes = GetUtf8ContentBytes(file.Content, file.ContentEncoding); await stream.WriteAsync(contentBytes, 0, contentBytes.Length); } } else if (file.Operation == GitFileOperation.Delete) { File.Delete(filePath); } LocalHelpers.ExecuteCommand(GitExecutable, $"add {filePath}", logger, clonedRepo); } LocalHelpers.ExecuteCommand(GitExecutable, $"commit -m \"{commitMessage}\"", logger, clonedRepo); LocalHelpers.ExecuteCommand(GitExecutable, $"-c core.askpass= -c credential.helper= push {remote} {branch}", logger, clonedRepo); } catch (Exception exc) { // This was originally a DarcException. Making it an actual Exception so we get to see in AppInsights if something failed while // commiting the changes throw new Exception($"Something went wrong when pushing the files to repo {repoUri} in branch {branch}", exc); } finally { try { // .git/objects hierarchy are marked as read-only so we need to unset the read-only attribute otherwise an UnauthorizedAccessException is thrown. GitFileManager.NormalizeAttributes(tempRepoFolder); Directory.Delete(tempRepoFolder, true); } catch (DirectoryNotFoundException) { // If the directory wasn't found, that means that the clone operation above failed // but this error isn't interesting at all. } catch (Exception exc) { throw new Exception($"Something went wrong while trying to delete the folder {tempRepoFolder}", exc); } } }