示例#1
0
        public static void Main(string[] args)
        {
            string     configFileName = "VersionUpdaterConfig.xml";
            string     configFileDir  = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath);
            string     configFilePath = Path.Combine(configFileDir, configFileName);
            ConfigFile config         = ConfigFile.Load(configFilePath);

            HashSet <PropertyInfo> configOverride;
            string                   gitPath;
            List <ProjectInfo>       allProjects;
            List <GitCommitInfo>     gitHistory;
            List <ProjectChangeInfo> changes;

            // Parse command line arguments and use them to override config properties
            configOverride = ParseCommandLine(config, args);
            string solutionDir = Path.GetDirectoryName(config.SolutionPath);

            // Write some diagnostic data to the log
            {
                Console.WriteLine("VersionUpdater launched");
                Console.WriteLine("Working Dir: {0}", Environment.CurrentDirectory);
                Console.WriteLine("Relative Base Dir: {0}", configFileDir);
                Console.WriteLine("Command Line: {0}", args.Aggregate("", (acc, arg) => acc + " " + arg));
                Console.WriteLine("Config:");
                foreach (PropertyInfo prop in typeof(ConfigFile).GetProperties())
                {
                    if (configOverride.Contains(prop))
                    {
                        Console.ForegroundColor = ConsoleColor.White;
                    }
                    Console.WriteLine("  {0}: {1}", prop.Name, prop.GetValue(config, null));
                    Console.ForegroundColor = ConsoleColor.Gray;
                }
                Console.WriteLine();
                Console.WriteLine();
            }

            // Update all config file paths to be absolute and account for the fact that
            // the working directory might be different from the one where executable and
            // config file are located
            UpdateConfigPaths(config, configFileDir);

            // Determine the path of a usable git executable
            gitPath = SearchGitPath(config);

            Console.WriteLine("Git executable path: '{0}'", gitPath);
            Console.WriteLine();

            // Check if there are any local changes in the git repo
            {
                bool             anyModifiedFiles = false;
                ProcessStartInfo gitStartInfo     = new ProcessStartInfo
                {
                    FileName               = gitPath,
                    WorkingDirectory       = solutionDir,
                    Arguments              = "--no-pager ls-files -m",
                    UseShellExecute        = false,
                    RedirectStandardOutput = true,
                    CreateNoWindow         = true,
                    WindowStyle            = ProcessWindowStyle.Hidden
                };
                Process gitProc = new Process();
                gitProc.StartInfo           = gitStartInfo;
                gitProc.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
                {
                    // If we receive any non-empty lines, those are modified files
                    if (!string.IsNullOrEmpty(e.Data))
                    {
                        anyModifiedFiles = true;
                        gitProc.CancelOutputRead();
                        if (!gitProc.HasExited)
                        {
                            gitProc.Kill();
                        }
                        return;
                    }
                };
                gitProc.Start();
                gitProc.BeginOutputReadLine();
                gitProc.WaitForExit();

                if (anyModifiedFiles)
                {
                    Console.WriteLine();
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("  There are modified files in your local git repository. Please make sure to start the version updater tool only with a clean working copy and no staged files.");
                    Console.ResetColor();
                    Console.WriteLine();
                    return;
                }
            }

            // Retrieve information about all project files and their nuspec associations
            allProjects = ParseProjectInfo(config);

            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("Found {0} projects:", allProjects.Count);
            Console.ResetColor();
            Console.WriteLine();
            int maxProjectNameLen = allProjects.Max(p => p.NuSpecPackageId.Length - p.NuSpecPackageId.LastIndexOf('.') - 1);

            foreach (ProjectInfo project in allProjects)
            {
                string[] nameTokens           = project.NuSpecPackageId.Split('.');
                string   displayedProjectName = nameTokens.LastOrDefault();

                Console.Write("  {0}", displayedProjectName.PadRight(maxProjectNameLen, ' '));
                Console.Write("{0,7}", project.NuSpecVersion);
                Console.ForegroundColor = ConsoleColor.DarkGray;
                Console.WriteLine(" in '{0}'", GetRelativePath(project.ProjectRootDir, solutionDir));
                Console.ResetColor();
            }
            Console.WriteLine();

            // Retrieve information about the git history since we last did a package update
            gitHistory = ParseGitCommitsSinceLastPackage(config, gitPath);

            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("Logged {0} commits since last package update:", gitHistory.Count);
            Console.ResetColor();
            Console.WriteLine();
            foreach (GitCommitInfo commit in gitHistory)
            {
                Console.ForegroundColor = ConsoleColor.DarkGray;
                Console.Write("  {0}: ", commit.Id);
                Console.ResetColor();
                Console.Write("'{0}', ", commit.Title);
                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine("{0} files changed", commit.FilePaths.Count);
                Console.ResetColor();
            }
            Console.WriteLine();

            // Determine the changes for each project individually and gather changelog entries
            changes = GetChangesPerProject(config, allProjects, gitHistory);

            // Display each change to the user and ask whether it should be considered a patch, minor or major release
            RetrieveUpdateModes(config, changes);

            // Apply the specified update modes to the version numbers of each project
            UpdateVersionNumbers(config, changes);

            // Remove change entries without an update
            changes.RemoveAll(c => c.UpdateMode == UpdateMode.None);

            // Apply version numbers and change logs to nuspecs and projects
            foreach (ProjectChangeInfo changeInfo in changes)
            {
                string versionString = string.Format("{0}.{1}.{2}",
                                                     changeInfo.Project.NuSpecVersion.Major,
                                                     changeInfo.Project.NuSpecVersion.Minor,
                                                     changeInfo.Project.NuSpecVersion.Build);

                // Update AssemblyInfo version
                {
                    string[] assemblyInfoLines = File.ReadAllLines(changeInfo.Project.AssemblyInfoFilePath);
                    for (int i = 0; i < assemblyInfoLines.Length; i++)
                    {
                        string line = assemblyInfoLines[i];
                        if (!line.Contains("AssemblyVersion") && !line.Contains("AssemblyFileVersion"))
                        {
                            continue;
                        }

                        int beginIndex = line.IndexOf('(');
                        int endIndex   = line.IndexOf(')');
                        if (beginIndex == -1)
                        {
                            continue;
                        }
                        if (endIndex == -1)
                        {
                            continue;
                        }

                        line =
                            line.Substring(0, beginIndex) +
                            "(\"" + versionString + "\")" +
                            line.Substring(endIndex + 1, line.Length - endIndex - 1);
                        assemblyInfoLines[i] = line;
                    }
                    File.WriteAllLines(changeInfo.Project.AssemblyInfoFilePath, assemblyInfoLines);
                }

                // Update nuspec version, release notes and dependencies
                {
                    XDocument nuspecDoc           = XDocument.Load(changeInfo.Project.NuSpecFilePath);
                    XElement  metadataElement     = nuspecDoc.Descendants("metadata").FirstOrDefault();
                    XElement  versionElement      = metadataElement.Descendants("version").FirstOrDefault();
                    XElement  releaseNotesElement = metadataElement.Descendants("releaseNotes").FirstOrDefault();
                    XElement  dependenciesElement = metadataElement.Descendants("dependencies").FirstOrDefault();
                    if (releaseNotesElement == null)
                    {
                        releaseNotesElement = new XElement("releaseNotes");
                        metadataElement.Add(releaseNotesElement);
                    }

                    versionElement.Value      = versionString;
                    releaseNotesElement.Value =
                        string.Join(", ", changeInfo.Titles.Take(3)) +
                        Environment.NewLine +
                        string.Join(Environment.NewLine, changeInfo.ChangeLog);
                    foreach (XElement dependencyElement in dependenciesElement.Descendants("dependency"))
                    {
                        XAttribute idAttrib      = dependencyElement.Attribute("id");
                        XAttribute versionAttrib = dependencyElement.Attribute("version");
                        if (idAttrib == null)
                        {
                            continue;
                        }
                        if (versionAttrib == null)
                        {
                            continue;
                        }

                        ProjectInfo dependency = allProjects.FirstOrDefault(p => string.Equals(p.NuSpecPackageId, idAttrib.Value, StringComparison.InvariantCultureIgnoreCase));
                        if (dependency == null)
                        {
                            continue;
                        }

                        versionAttrib.Value = string.Format("{0}.{1}.{2}",
                                                            dependency.NuSpecVersion.Major,
                                                            dependency.NuSpecVersion.Minor,
                                                            dependency.NuSpecVersion.Build);
                    }

                    nuspecDoc.Save(changeInfo.Project.NuSpecFilePath);
                }
            }

            // Perform a git commit with an auto-generated message
            if (changes.Count > 0)
            {
                Console.WriteLine();
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Performing Git Commit...");
                Console.ResetColor();
                Console.WriteLine();

                string commitMsgFileName = "PackageUpdateCommitMsg.txt";
                string commitMsgFilePath = Path.GetFullPath(Path.Combine(solutionDir, commitMsgFileName));

                // Build the commit message
                StringBuilder messageBuilder = new StringBuilder();
                messageBuilder.AppendLine(PackageUpdateCommitTitle);
                foreach (ProjectChangeInfo changeInfo in changes)
                {
                    string versionString = string.Format("{0}.{1}.{2}",
                                                         changeInfo.Project.NuSpecVersion.Major,
                                                         changeInfo.Project.NuSpecVersion.Minor,
                                                         changeInfo.Project.NuSpecVersion.Build);
                    messageBuilder.AppendFormat(PackageUpdateCommitMessage, changeInfo.Project.NuSpecPackageId, versionString);
                    messageBuilder.AppendLine();
                }
                File.WriteAllText(commitMsgFilePath, messageBuilder.ToString());

                // Stage all files in git
                {
                    ProcessStartInfo gitStartInfo = new ProcessStartInfo
                    {
                        FileName         = gitPath,
                        WorkingDirectory = solutionDir,
                        Arguments        = "add -u",
                        UseShellExecute  = false,
                    };
                    Process gitProc = Process.Start(gitStartInfo);
                    gitProc.WaitForExit();
                }

                // Execute a git commit
                {
                    ProcessStartInfo gitStartInfo = new ProcessStartInfo
                    {
                        FileName         = gitPath,
                        WorkingDirectory = solutionDir,
                        Arguments        = "commit -F " + commitMsgFileName,
                        UseShellExecute  = false,
                    };
                    Process gitProc = Process.Start(gitStartInfo);
                    gitProc.WaitForExit();
                }

                // Remove our temporary commit message file
                File.Delete(commitMsgFilePath);
            }

            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("All done!");
            Console.WriteLine();
        }