/// <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);
        }
Beispiel #2
0
        /// <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);
                }
            }
        }