public SemanticReleaseNotes Merge(SemanticReleaseNotes previousReleaseNotes) { var semanticReleases = previousReleaseNotes.Releases .Where(r => Releases.All(r2 => r.ReleaseName != r2.ReleaseName)) .Select(CreateMergedSemanticRelease); var enumerable = Releases.Select(CreateMergedSemanticRelease); var mergedReleases = enumerable .Union(semanticReleases) .ToArray(); foreach (var semanticRelease in mergedReleases) { var releaseFromThis = Releases.SingleOrDefault(r => r.ReleaseName == semanticRelease.ReleaseName); var releaseFromPrevious = previousReleaseNotes.Releases.SingleOrDefault(r => r.ReleaseName == semanticRelease.ReleaseName); if (releaseFromThis != null) { semanticRelease.ReleaseNoteLines.AddRange(releaseFromThis.ReleaseNoteLines); } if (releaseFromPrevious != null) { semanticRelease.ReleaseNoteLines.AddRange(releaseFromPrevious.ReleaseNoteLines); } } return(new SemanticReleaseNotes(mergedReleases, new Categories(categories.AvailableCategories.Union(previousReleaseNotes.categories.AvailableCategories).Distinct().ToArray(), categories.AllLabels))); }
public static SemanticReleaseNotes GenerateReleaseNotes(Context context, IRepository gitRepo, IIssueTracker issueTracker, SemanticReleaseNotes previousReleaseNotes, Categories categories, TaggedCommit tagToStartFrom, ReleaseInfo currentReleaseInfo) { var releases = ReleaseFinder.FindReleases(gitRepo, tagToStartFrom, currentReleaseInfo); var findIssuesSince = IssueStartDateBasedOnPreviousReleaseNotes(gitRepo, previousReleaseNotes) ?? tagToStartFrom.Commit.Author.When; var closedIssues = issueTracker.GetClosedIssues(context.IssueTracker, findIssuesSince).ToArray(); var semanticReleases = ( from release in releases let releaseNoteItems = closedIssues .Where(i => (release.When == null || i.DateClosed < release.When) && (release.PreviousReleaseDate == null || i.DateClosed > release.PreviousReleaseDate)) .Select(i => new ReleaseNoteItem(i.Title, i.Id, i.HtmlUrl, i.Labels, i.DateClosed, i.Contributors)) .ToList<IReleaseNoteLine>() let beginningSha = release.FirstCommit == null ? null : release.FirstCommit.Substring(0, 10) let endSha = release.LastCommit == null ? null : release.LastCommit.Substring(0, 10) select new SemanticRelease(release.Name, release.When, releaseNoteItems, new ReleaseDiffInfo { BeginningSha = beginningSha, EndSha = endSha, //DiffUrlFormat = issueTracker.DiffUrlFormat })).ToList(); return new SemanticReleaseNotes(semanticReleases, categories).Merge(previousReleaseNotes); }
private static DateTimeOffset?IssueStartDateBasedOnPreviousReleaseNotes(IRepository gitRepo, SemanticReleaseNotes previousReleaseNotes) { var lastGeneratedRelease = previousReleaseNotes.Releases.FirstOrDefault(); if (lastGeneratedRelease == null) { return(null); } var endSha = lastGeneratedRelease.DiffInfo.EndSha; if (string.IsNullOrEmpty(endSha)) { lastGeneratedRelease = previousReleaseNotes.Releases.Skip(1).FirstOrDefault(); if (lastGeneratedRelease != null) { endSha = lastGeneratedRelease.DiffInfo.EndSha; } } if (string.IsNullOrEmpty(endSha)) { return(null); } var commitToStartFrom = gitRepo.Commits.FirstOrDefault(c => c.Sha.StartsWith(endSha)); if (commitToStartFrom != null) { return(commitToStartFrom.Author.When); } return(null); }
public static SemanticReleaseNotes GenerateReleaseNotes( IRepository gitRepo, IIssueTracker issueTracker, SemanticReleaseNotes previousReleaseNotes, Categories categories, TaggedCommit tagToStartFrom, ReleaseInfo currentReleaseInfo, string diffUrlFormat) { var releases = ReleaseFinder.FindReleases(gitRepo, tagToStartFrom, currentReleaseInfo); var findIssuesSince = IssueStartDateBasedOnPreviousReleaseNotes(gitRepo, previousReleaseNotes) ?? tagToStartFrom.Commit.Author.When; var closedIssues = issueTracker.GetClosedIssues(findIssuesSince).ToArray(); var semanticReleases = ( from release in releases let releaseNoteItems = closedIssues .Where(i => (release.When == null || i.DateClosed < release.When) && (release.PreviousReleaseDate == null || i.DateClosed > release.PreviousReleaseDate)) .Select(i => new ReleaseNoteItem(i.Title, i.Id, i.HtmlUrl, i.Labels, i.DateClosed, i.Contributors)) .ToList <IReleaseNoteLine>() let beginningSha = release.FirstCommit == null ? null : release.FirstCommit.Substring(0, 10) let endSha = release.LastCommit == null ? null : release.LastCommit.Substring(0, 10) select new SemanticRelease(release.Name, release.When, releaseNoteItems, new ReleaseDiffInfo { BeginningSha = beginningSha, EndSha = endSha, DiffUrlFormat = diffUrlFormat })).ToList(); return(new SemanticReleaseNotes(semanticReleases, categories).Merge(previousReleaseNotes)); }
public async Task <SemanticReleaseNotes> GenerateReleaseNotesAsync(SemanticReleaseNotes releaseNotesToUpdate) { var gitRepository = new Repository(Repository.Discover(_generationParameters.WorkingDirectory)); IIssueTracker issueTracker = null; var categories = new Categories(_generationParameters.Categories, _generationParameters.AllLabels); var tagToStartFrom = _generationParameters.AllTags ? gitRepository.GetFirstCommit() : gitRepository.GetLastTaggedCommit() ?? gitRepository.GetFirstCommit(); var currentReleaseInfo = gitRepository.GetCurrentReleaseInfo(); if (!string.IsNullOrEmpty(_generationParameters.Version)) { currentReleaseInfo.Name = _generationParameters.Version; currentReleaseInfo.When = DateTimeOffset.Now; } else { currentReleaseInfo.Name = "vNext"; } if (_generationParameters.UseIssueTracker) { if (_generationParameters.IssueTracker.Type.HasValue) { issueTracker = IssueTrackerFactory.CreateIssueTracker(new IssueTrackerSettings(_generationParameters.IssueTracker.Server, _generationParameters.IssueTracker.Type.Value) { Project = _generationParameters.IssueTracker.ProjectId, Authentication = _generationParameters.IssueTracker.Authentication.ToIssueTrackerSettings() }); } else { if (!TryRemote(gitRepository, "upstream", _generationParameters, out issueTracker) && !TryRemote(gitRepository, "origin", _generationParameters, out issueTracker)) { throw new Exception("Unable to guess issue tracker through remote, specify issue tracker type on the command line"); } } } var releaseNotes = _generationParameters.UseIssueTracker ? await GenerateReleaseNotesAsync(_generationParameters, gitRepository, issueTracker, releaseNotesToUpdate, categories, tagToStartFrom, currentReleaseInfo) : await GenerateReleaseNotesAsync( _generationParameters, gitRepository, releaseNotesToUpdate, categories, tagToStartFrom, currentReleaseInfo); return(releaseNotes); }
public async Task<SemanticReleaseNotes> GenerateReleaseNotesAsync(SemanticReleaseNotes releaseNotesToUpdate) { var gitRepository = new Repository(Repository.Discover(_generationParameters.WorkingDirectory)); IIssueTracker issueTracker; if (_generationParameters.IssueTracker.Type.HasValue) { issueTracker = IssueTrackerFactory.CreateIssueTracker(new IssueTrackerSettings(_generationParameters.IssueTracker.Server, _generationParameters.IssueTracker.Type.Value) { Project = _generationParameters.IssueTracker.ProjectId, Authentication = _generationParameters.IssueTracker.Authentication.ToIssueTrackerSettings() }); } else { if (!TryRemote(gitRepository, "upstream", _generationParameters, out issueTracker) && !TryRemote(gitRepository, "origin", _generationParameters, out issueTracker)) { throw new Exception("Unable to guess issue tracker through remote, specify issue tracker type on the command line"); } } var categories = new Categories(_generationParameters.Categories, _generationParameters.AllLabels); var tagToStartFrom = _generationParameters.AllTags ? gitRepository.GetFirstCommit() : gitRepository.GetLastTaggedCommit() ?? gitRepository.GetFirstCommit(); var currentReleaseInfo = gitRepository.GetCurrentReleaseInfo(); if (!string.IsNullOrEmpty(_generationParameters.Version)) { currentReleaseInfo.Name = _generationParameters.Version; currentReleaseInfo.When = DateTimeOffset.Now; } else { currentReleaseInfo.Name = "vNext"; } var releaseNotes = await GenerateReleaseNotesAsync( _generationParameters, gitRepository, issueTracker, releaseNotesToUpdate, categories, tagToStartFrom, currentReleaseInfo); return releaseNotes; }
public void WriteReleaseNotes(GitReleaseNotesArguments arguments, SemanticReleaseNotes releaseNotes) { var builder = new StringBuilder(); var categories = arguments.Categories == null ? _categories : _categories.Concat(arguments.Categories.Split(',')).ToArray(); foreach (var releaseNoteItem in releaseNotes.ReleaseNoteItems) { var taggedCategory = releaseNoteItem.Tags .FirstOrDefault(t => categories.Any(c => c.Equals(t, StringComparison.InvariantCultureIgnoreCase))); var title = releaseNoteItem.Title; var issueNumber = releaseNoteItem.IssueNumber; var htmlUrl = releaseNoteItem.HtmlUrl; if ("bug".Equals(taggedCategory, StringComparison.InvariantCultureIgnoreCase)) taggedCategory = "fix"; var category = taggedCategory == null ? null : string.Format(" +{0}", taggedCategory.Replace(" ", "-")); var item = string.Format(" - {0} [{1}]({2}){3}", title, issueNumber, htmlUrl, category); builder.AppendLine(item); } var outputFile = Path.IsPathRooted(arguments.OutputFile) ? arguments.OutputFile : Path.Combine(_workingDirectory, arguments.OutputFile); _fileSystem.WriteAllText(outputFile, builder.ToString()); }
public static Task <SemanticReleaseNotes> GenerateReleaseNotesAsync(ReleaseNotesGenerationParameters generationParameters, IRepository gitRepo, SemanticReleaseNotes previousReleaseNotes, Categories categories, TaggedCommit tagToStartFrom, ReleaseInfo currentReleaseInfo) { var issuenumberregex = new Regex(@"#\s?(\d+)", RegexOptions.Compiled); var semanticReleases = new Dictionary <string, SemanticRelease>(); var releases = ReleaseFinder.FindReleases(gitRepo, tagToStartFrom, currentReleaseInfo); foreach (var release in releases) { if (!semanticReleases.ContainsKey(release.Name)) { var beginningSha = release.FirstCommit != null?release.FirstCommit.Substring(0, 10) : null; var endSha = release.LastCommit != null?release.LastCommit.Substring(0, 10) : null; semanticReleases.Add(release.Name, new SemanticRelease(release.Name, release.When, new ReleaseDiffInfo { BeginningSha = beginningSha, EndSha = endSha, // TODO DiffUrlFormat = context.Repository.DiffUrlFormat })); } var semanticRelease = semanticReleases[release.Name]; var commits = gitRepo.Commits.Where(x => x.Author.When >= release.PreviousReleaseDate && x.Author.When <= release.When); foreach (Commit commit in commits) { var match = issuenumberregex.Match(commit.Message); var releaseNoteItem = new ReleaseNoteItem(match.Success ? commit.Message.Replace(match.Groups[0].Value, "") :commit.Message, match.Success ? match.Groups[0].Value : string.Empty, match.Success? string.Format(generationParameters.IssueTracker.Server, match.Groups[0].Value): string.Empty, null, commit.Author.When, new[] { new Contributor(commit.Author.Name, commit.Author.Email, string.Empty) }); semanticRelease.ReleaseNoteLines.Add(releaseNoteItem); } } var semanticReleaseNotes = new SemanticReleaseNotes(semanticReleases.Values, categories); var mergedReleaseNotes = semanticReleaseNotes.Merge(previousReleaseNotes); return(Task.FromResult(mergedReleaseNotes)); }
private static DateTimeOffset? IssueStartDateBasedOnPreviousReleaseNotes(IRepository gitRepo, SemanticReleaseNotes previousReleaseNotes) { var lastGeneratedRelease = previousReleaseNotes.Releases.FirstOrDefault(); if (lastGeneratedRelease == null) return null; var endSha = lastGeneratedRelease.DiffInfo.EndSha; if (string.IsNullOrEmpty(endSha)) { lastGeneratedRelease = previousReleaseNotes.Releases.Skip(1).FirstOrDefault(); if (lastGeneratedRelease != null) { endSha = lastGeneratedRelease.DiffInfo.EndSha; } } if (string.IsNullOrEmpty(endSha)) return null; var commitToStartFrom = gitRepo.Commits.FirstOrDefault(c => c.Sha.StartsWith(endSha)); if (commitToStartFrom != null) { return commitToStartFrom.Author.When; } return null; }
public static int Main(string[] args) { GitReleaseNotesEnvironment.Log = new ConsoleLog(); var modelBindingDefinition = Configuration.Configure <GitReleaseNotesArguments>(); if (!args.Any() || args.Any(a => a == "/?" || a == "?" || a.Equals("/help", StringComparison.InvariantCultureIgnoreCase))) { ShowHelp(modelBindingDefinition); return(0); } var exitCode = 0; var arguments = modelBindingDefinition.CreateAndBind(args); if (string.IsNullOrEmpty(arguments.OutputFile)) { ShowHelp(modelBindingDefinition); return(1); } var context = arguments.ToContext(); //if (!context.Validate()) //{ // return -1; //} try { var fileSystem = new FileSystem.FileSystem(); var releaseFileWriter = new ReleaseFileWriter(fileSystem); string outputFile = null; var previousReleaseNotes = new SemanticReleaseNotes(); var outputPath = context.WorkingDirectory; var outputDirectory = new DirectoryInfo(outputPath); if (outputDirectory.Name == ".git") { outputPath = outputDirectory.Parent.FullName; } if (!string.IsNullOrEmpty(arguments.OutputFile)) { outputFile = Path.IsPathRooted(arguments.OutputFile) ? arguments.OutputFile : Path.Combine(outputPath, arguments.OutputFile); previousReleaseNotes = new ReleaseNotesFileReader(fileSystem, outputPath).ReadPreviousReleaseNotes(outputFile); } var releaseNotesGenerator = new ReleaseNotesGenerator(context); var releaseNotes = releaseNotesGenerator.GenerateReleaseNotesAsync(previousReleaseNotes).Result; var releaseNotesOutput = releaseNotes.ToString(); releaseFileWriter.OutputReleaseNotesFile(releaseNotesOutput, outputFile); Log.WriteLine("Done"); } catch (GitReleaseNotesException ex) { exitCode = -1; Log.WriteLine("An expected error occurred: {0}", ex.Message); } catch (Exception ex) { exitCode = -2; Log.WriteLine("An unexpected error occurred: {0}", ex.Message); } if (Debugger.IsAttached) { Console.WriteLine("Press any key to continue"); Console.ReadKey(); } return(exitCode); }
static int Main(string[] args) { GitReleaseNotesEnvironment.Log = new ConsoleLog(); var modelBindingDefinition = Configuration.Configure<GitReleaseNotesArguments>(); if (args.Any(a => a == "/?" || a == "?" || a.Equals("/help", StringComparison.InvariantCultureIgnoreCase))) { ShowHelp(modelBindingDefinition); return 0; } var exitCode = 0; var arguments = modelBindingDefinition.CreateAndBind(args); if (string.IsNullOrEmpty(arguments.OutputFile)) { ShowHelp(modelBindingDefinition); return 1; } var context = arguments.ToContext(); //if (!context.Validate()) //{ // return -1; //} try { var fileSystem = new FileSystem.FileSystem(); var releaseFileWriter = new ReleaseFileWriter(fileSystem); string outputFile = null; var previousReleaseNotes = new SemanticReleaseNotes(); var outputPath = context.WorkingDirectory; var outputDirectory = new DirectoryInfo(outputPath); if (outputDirectory.Name == ".git") { outputPath = outputDirectory.Parent.FullName; } if (!string.IsNullOrEmpty(arguments.OutputFile)) { outputFile = Path.IsPathRooted(arguments.OutputFile) ? arguments.OutputFile : Path.Combine(outputPath, arguments.OutputFile); previousReleaseNotes = new ReleaseNotesFileReader(fileSystem, outputPath).ReadPreviousReleaseNotes(outputFile); } var releaseNotesGenerator = new ReleaseNotesGenerator(context); var releaseNotes = releaseNotesGenerator.GenerateReleaseNotesAsync(previousReleaseNotes).Result; var releaseNotesOutput = releaseNotes.ToString(); releaseFileWriter.OutputReleaseNotesFile(releaseNotesOutput, outputFile); Log.WriteLine("Done"); } catch (GitReleaseNotesException ex) { exitCode = -1; Log.WriteLine("An expected error occurred: {0}", ex.Message); } catch (Exception ex) { exitCode = -2; Log.WriteLine("An unexpected error occurred: {0}", ex.Message); } if (Debugger.IsAttached) { Console.WriteLine("Press any key to continue"); Console.ReadKey(); } return exitCode; }
public SemanticReleaseNotes GenerateReleaseNotes() { var context = _context; using (var gitRepoContext = GetRepository(context, _fileSystem)) { // Remote repo's require some additional preparation before first use. if (gitRepoContext.IsRemote) { gitRepoContext.PrepareRemoteRepoForUse(context.Repository.Branch); if (!string.IsNullOrWhiteSpace(context.OutputFile)) { gitRepoContext.CheckoutFilesIfExist(context.OutputFile); } } var gitRepo = gitRepoContext.Repository; if (context.IssueTracker == null) { // TODO: Write auto detection mechanism which is better than this throw new GitReleaseNotesException("Feature to automatically detect issue tracker must be written"); //var firstOrDefault = _issueTrackers.FirstOrDefault(i => i.Value.RemotePresentWhichMatches); //if (firstOrDefault.Value != null) //{ // issueTracker = firstOrDefault.Value; //} } var issueTracker = _issueTrackerFactory.CreateIssueTracker(context, gitRepo); if (issueTracker == null) { throw new GitReleaseNotesException("Failed to create issue tracker from context, cannot continue"); } var releaseFileWriter = new ReleaseFileWriter(_fileSystem); string outputFile = null; var previousReleaseNotes = new SemanticReleaseNotes(); var outputPath = gitRepo.Info.Path; var outputDirectory = new DirectoryInfo(outputPath); if (outputDirectory.Name == ".git") { outputPath = outputDirectory.Parent.FullName; } if (!string.IsNullOrEmpty(context.OutputFile)) { outputFile = Path.IsPathRooted(context.OutputFile) ? context.OutputFile : Path.Combine(outputPath, context.OutputFile); previousReleaseNotes = new ReleaseNotesFileReader(_fileSystem, outputPath).ReadPreviousReleaseNotes(outputFile); } var categories = new Categories(context.Categories, context.AllLabels); var tagToStartFrom = context.AllTags ? GitRepositoryInfoFinder.GetFirstCommit(gitRepo) : GitRepositoryInfoFinder.GetLastTaggedCommit(gitRepo) ?? GitRepositoryInfoFinder.GetFirstCommit(gitRepo); var currentReleaseInfo = GitRepositoryInfoFinder.GetCurrentReleaseInfo(gitRepo); if (!string.IsNullOrEmpty(context.Version)) { currentReleaseInfo.Name = context.Version; currentReleaseInfo.When = DateTimeOffset.Now; } var releaseNotes = GenerateReleaseNotes( context, gitRepo, issueTracker, previousReleaseNotes, categories, tagToStartFrom, currentReleaseInfo); var releaseNotesOutput = releaseNotes.ToString(); releaseFileWriter.OutputReleaseNotesFile(releaseNotesOutput, outputFile); return releaseNotes; } }
private static int GenerateReleaseNotes(string[] args) { var modelBindingDefinition = Args.Configuration.Configure<GitReleaseNotesArguments>(); if (args.Any(a => a == "/?" || a == "?" || a.Equals("/help", StringComparison.InvariantCultureIgnoreCase))) { var help = new HelpProvider().GenerateModelHelp(modelBindingDefinition); var f = new ConsoleHelpFormatter(); f.WriteHelp(help, Console.Out); return 0; } var arguments = modelBindingDefinition.CreateAndBind(args); if (!ArgumentVerifier.VerifyArguments(arguments)) { return 1; } var workingDirectory = arguments.WorkingDirectory ?? Directory.GetCurrentDirectory(); var gitDirectory = GitDirFinder.TreeWalkForGitDir(workingDirectory); if (string.IsNullOrEmpty(gitDirectory)) { throw new Exception("Failed to find .git directory."); } Console.WriteLine("Git directory found at {0}", gitDirectory); var repositoryRoot = Directory.GetParent(gitDirectory).FullName; var gitRepo = new Repository(gitDirectory); CreateIssueTrackers(gitRepo, arguments); IIssueTracker issueTracker = null; if (arguments.IssueTracker == null) { var firstOrDefault = _issueTrackers.FirstOrDefault(i => i.Value.RemotePresentWhichMatches); if (firstOrDefault.Value != null) issueTracker = firstOrDefault.Value; } if (issueTracker == null) { if (!_issueTrackers.ContainsKey(arguments.IssueTracker.Value)) throw new Exception(string.Format("{0} is not a known issue tracker", arguments.IssueTracker.Value)); issueTracker = _issueTrackers[arguments.IssueTracker.Value]; } if (!issueTracker.VerifyArgumentsAndWriteErrorsToConsole()) { return 1; } var fileSystem = new FileSystem.FileSystem(); var releaseFileWriter = new ReleaseFileWriter(fileSystem); string outputFile = null; var previousReleaseNotes = new SemanticReleaseNotes(); if (!string.IsNullOrEmpty(arguments.OutputFile)) { outputFile = Path.IsPathRooted(arguments.OutputFile) ? arguments.OutputFile : Path.Combine(repositoryRoot, arguments.OutputFile); previousReleaseNotes = new ReleaseNotesFileReader(fileSystem, repositoryRoot).ReadPreviousReleaseNotes(outputFile); } var categories = arguments.Categories == null ? Categories : Categories.Concat(arguments.Categories.Split(',')).ToArray(); TaggedCommit tagToStartFrom = arguments.AllTags ? GitRepositoryInfoFinder.GetFirstCommit(gitRepo) : GitRepositoryInfoFinder.GetLastTaggedCommit(gitRepo) ?? GitRepositoryInfoFinder.GetFirstCommit(gitRepo); var currentReleaseInfo = GitRepositoryInfoFinder.GetCurrentReleaseInfo(gitRepo); if (!string.IsNullOrEmpty(arguments.Version)) { currentReleaseInfo.Name = arguments.Version; currentReleaseInfo.When = DateTimeOffset.Now; } var releaseNotes = ReleaseNotesGenerator.GenerateReleaseNotes( gitRepo, issueTracker, previousReleaseNotes, categories, tagToStartFrom, currentReleaseInfo, issueTracker.DiffUrlFormat); var releaseNotesOutput = releaseNotes.ToString(); releaseFileWriter.OutputReleaseNotesFile(releaseNotesOutput, outputFile); PublishReleaseIfNeeded(releaseNotesOutput, arguments, issueTracker); return 0; }
public SemanticReleaseNotes Merge(SemanticReleaseNotes previousReleaseNotes) { var semanticReleases = previousReleaseNotes.Releases .Where(r => Releases.All(r2 => r.ReleaseName != r2.ReleaseName)) .Select(CreateMergedSemanticRelease); var enumerable = Releases.Select(CreateMergedSemanticRelease); var mergedReleases = enumerable .Union(semanticReleases) .ToArray(); foreach (var semanticRelease in mergedReleases) { var releaseFromThis = Releases.SingleOrDefault(r => r.ReleaseName == semanticRelease.ReleaseName); var releaseFromPrevious = previousReleaseNotes.Releases.SingleOrDefault(r => r.ReleaseName == semanticRelease.ReleaseName); if (releaseFromThis != null) { semanticRelease.ReleaseNoteLines.AddRange(releaseFromThis.ReleaseNoteLines); } if (releaseFromPrevious != null) { semanticRelease.ReleaseNoteLines.AddRange(releaseFromPrevious.ReleaseNoteLines); } } return new SemanticReleaseNotes(mergedReleases, new Categories(categories.AvailableCategories.Union(previousReleaseNotes.categories.AvailableCategories).Distinct().ToArray(), categories.AllLabels)); }
public static SemanticReleaseNotes GenerateReleaseNotes(Context context) { using (var gitRepoContext = GetRepository(context)) { // Remote repo's require some additional preparation before first use. if (gitRepoContext.IsRemote) { gitRepoContext.PrepareRemoteRepoForUse(context.Repository.Branch); if (!string.IsNullOrWhiteSpace(context.OutputFile)) { gitRepoContext.CheckoutFilesIfExist(context.OutputFile); } } var gitRepo = gitRepoContext.Repository; CreateIssueTrackers(gitRepo, context); IIssueTracker issueTracker = null; if (context.IssueTracker == null) { var firstOrDefault = _issueTrackers.FirstOrDefault(i => i.Value.RemotePresentWhichMatches); if (firstOrDefault.Value != null) { issueTracker = firstOrDefault.Value; } } if (issueTracker == null) { if (!_issueTrackers.ContainsKey(context.IssueTracker.Value)) { throw new GitReleaseNotesException("{0} is not a known issue tracker", context.IssueTracker.Value); } issueTracker = _issueTrackers[context.IssueTracker.Value]; } if (!issueTracker.VerifyArgumentsAndWriteErrorsToLog()) { throw new GitReleaseNotesException("Argument verification failed"); } var fileSystem = new FileSystem.FileSystem(); var releaseFileWriter = new ReleaseFileWriter(fileSystem); string outputFile = null; var previousReleaseNotes = new SemanticReleaseNotes(); var outputPath = gitRepo.Info.Path; var outputDirectory = new DirectoryInfo(outputPath); if (outputDirectory.Name == ".git") { outputPath = outputDirectory.Parent.FullName; } if (!string.IsNullOrEmpty(context.OutputFile)) { outputFile = Path.IsPathRooted(context.OutputFile) ? context.OutputFile : Path.Combine(outputPath, context.OutputFile); previousReleaseNotes = new ReleaseNotesFileReader(fileSystem, outputPath).ReadPreviousReleaseNotes(outputFile); } var categories = new Categories(context.Categories, context.AllLabels); var tagToStartFrom = context.AllTags ? GitRepositoryInfoFinder.GetFirstCommit(gitRepo) : GitRepositoryInfoFinder.GetLastTaggedCommit(gitRepo) ?? GitRepositoryInfoFinder.GetFirstCommit(gitRepo); var currentReleaseInfo = GitRepositoryInfoFinder.GetCurrentReleaseInfo(gitRepo); if (!string.IsNullOrEmpty(context.Version)) { currentReleaseInfo.Name = context.Version; currentReleaseInfo.When = DateTimeOffset.Now; } var releaseNotes = GenerateReleaseNotes( gitRepo, issueTracker, previousReleaseNotes, categories, tagToStartFrom, currentReleaseInfo, issueTracker.DiffUrlFormat); var releaseNotesOutput = releaseNotes.ToString(); releaseFileWriter.OutputReleaseNotesFile(releaseNotesOutput, outputFile); return(releaseNotes); } }
private static int GenerateReleaseNotes(string[] args) { var modelBindingDefinition = Args.Configuration.Configure <GitReleaseNotesArguments>(); if (args.Any(a => a == "/?" || a == "?" || a.Equals("/help", StringComparison.InvariantCultureIgnoreCase))) { var help = new HelpProvider().GenerateModelHelp(modelBindingDefinition); var f = new ConsoleHelpFormatter(); f.WriteHelp(help, Console.Out); return(0); } var arguments = modelBindingDefinition.CreateAndBind(args); if (!ArgumentVerifier.VerifyArguments(arguments)) { return(1); } var workingDirectory = arguments.WorkingDirectory ?? Directory.GetCurrentDirectory(); var gitDirectory = GitDirFinder.TreeWalkForGitDir(workingDirectory); if (string.IsNullOrEmpty(gitDirectory)) { throw new Exception("Failed to find .git directory."); } Console.WriteLine("Git directory found at {0}", gitDirectory); var repositoryRoot = Directory.GetParent(gitDirectory).FullName; var gitRepo = new Repository(gitDirectory); CreateIssueTrackers(gitRepo, arguments); IIssueTracker issueTracker = null; if (arguments.IssueTracker == null) { var firstOrDefault = _issueTrackers.FirstOrDefault(i => i.Value.RemotePresentWhichMatches); if (firstOrDefault.Value != null) { issueTracker = firstOrDefault.Value; } } if (issueTracker == null) { if (!_issueTrackers.ContainsKey(arguments.IssueTracker.Value)) { throw new Exception(string.Format("{0} is not a known issue tracker", arguments.IssueTracker.Value)); } issueTracker = _issueTrackers[arguments.IssueTracker.Value]; } if (!issueTracker.VerifyArgumentsAndWriteErrorsToConsole()) { return(1); } var fileSystem = new FileSystem.FileSystem(); var releaseFileWriter = new ReleaseFileWriter(fileSystem); string outputFile = null; var previousReleaseNotes = new SemanticReleaseNotes(); if (!string.IsNullOrEmpty(arguments.OutputFile)) { outputFile = Path.IsPathRooted(arguments.OutputFile) ? arguments.OutputFile : Path.Combine(repositoryRoot, arguments.OutputFile); previousReleaseNotes = new ReleaseNotesFileReader(fileSystem, repositoryRoot).ReadPreviousReleaseNotes(outputFile); } var categories = arguments.Categories == null ? Categories : Categories.Concat(arguments.Categories.Split(',')).ToArray(); TaggedCommit tagToStartFrom = arguments.AllTags ? GitRepositoryInfoFinder.GetFirstCommit(gitRepo) : GitRepositoryInfoFinder.GetLastTaggedCommit(gitRepo) ?? GitRepositoryInfoFinder.GetFirstCommit(gitRepo); var currentReleaseInfo = GitRepositoryInfoFinder.GetCurrentReleaseInfo(gitRepo); if (!string.IsNullOrEmpty(arguments.Version)) { currentReleaseInfo.Name = arguments.Version; currentReleaseInfo.When = DateTimeOffset.Now; } var releaseNotes = ReleaseNotesGenerator.GenerateReleaseNotes( gitRepo, issueTracker, previousReleaseNotes, categories, tagToStartFrom, currentReleaseInfo, issueTracker.DiffUrlFormat); var releaseNotesOutput = releaseNotes.ToString(); releaseFileWriter.OutputReleaseNotesFile(releaseNotesOutput, outputFile); return(0); }
public static async Task <SemanticReleaseNotes> GenerateReleaseNotesAsync(ReleaseNotesGenerationParameters generationParameters, IRepository gitRepo, IIssueTracker issueTracker, SemanticReleaseNotes previousReleaseNotes, Categories categories, TaggedCommit tagToStartFrom, ReleaseInfo currentReleaseInfo) { var releases = ReleaseFinder.FindReleases(gitRepo, tagToStartFrom, currentReleaseInfo); var findIssuesSince = IssueStartDateBasedOnPreviousReleaseNotes(gitRepo, previousReleaseNotes) ?? tagToStartFrom.Commit.Author.When; var filter = new IssueTrackerFilter { Since = findIssuesSince, IncludeOpen = false }; var closedIssues = (await issueTracker.GetIssuesAsync(filter)).ToArray(); // As discussed here: https://github.com/GitTools/GitReleaseNotes/issues/85 var semanticReleases = new Dictionary <string, SemanticRelease>(); foreach (var issue in closedIssues) { // 1) Include all issues from the issue tracker that are assigned to this release foreach (var fixVersion in issue.FixVersions) { if (!fixVersion.IsReleased) { continue; } if (!semanticReleases.ContainsKey(fixVersion.Name)) { semanticReleases.Add(fixVersion.Name, new SemanticRelease(fixVersion.Name, fixVersion.ReleaseDate)); } var semanticRelease = semanticReleases[fixVersion.Name]; var releaseNoteItem = new ReleaseNoteItem(issue.Title, issue.Id, issue.Url, issue.Labels, issue.DateClosed, new Contributor[] { /*TODO: implement*/ }); semanticRelease.ReleaseNoteLines.Add(releaseNoteItem); } // 2) Get closed issues from the issue tracker that have no fixversion but are closed between the last release and this release if (issue.FixVersions.Count == 0) { foreach (var release in releases) { if (issue.DateClosed.HasValue && issue.DateClosed.Value > release.PreviousReleaseDate && (release.When == null || issue.DateClosed <= release.When)) { if (!semanticReleases.ContainsKey(release.Name)) { var beginningSha = release.FirstCommit != null?release.FirstCommit.Substring(0, 10) : null; var endSha = release.LastCommit != null?release.LastCommit.Substring(0, 10) : null; semanticReleases.Add(release.Name, new SemanticRelease(release.Name, release.When, new ReleaseDiffInfo { BeginningSha = beginningSha, EndSha = endSha, // TODO DiffUrlFormat = context.Repository.DiffUrlFormat })); } var semanticRelease = semanticReleases[release.Name]; var releaseNoteItem = new ReleaseNoteItem(issue.Title, issue.Id, issue.Url, issue.Labels, issue.DateClosed, issue.Contributors); semanticRelease.ReleaseNoteLines.Add(releaseNoteItem); } } } } // 3) Remove any duplicates foreach (var semanticRelease in semanticReleases.Values) { var handledIssues = new HashSet <string>(); for (var i = 0; i < semanticRelease.ReleaseNoteLines.Count; i++) { var releaseNoteLine = semanticRelease.ReleaseNoteLines[i] as ReleaseNoteItem; if (releaseNoteLine == null) { continue; } if (handledIssues.Contains(releaseNoteLine.IssueNumber)) { semanticRelease.ReleaseNoteLines.RemoveAt(i--); continue; } handledIssues.Add(releaseNoteLine.IssueNumber); } } var semanticReleaseNotes = new SemanticReleaseNotes(semanticReleases.Values, categories); var mergedReleaseNotes = semanticReleaseNotes.Merge(previousReleaseNotes); return(mergedReleaseNotes); }
public static async Task<SemanticReleaseNotes> GenerateReleaseNotesAsync(ReleaseNotesGenerationParameters generationParameters, IRepository gitRepo, IIssueTracker issueTracker, SemanticReleaseNotes previousReleaseNotes, Categories categories, TaggedCommit tagToStartFrom, ReleaseInfo currentReleaseInfo) { var releases = ReleaseFinder.FindReleases(gitRepo, tagToStartFrom, currentReleaseInfo); var findIssuesSince = IssueStartDateBasedOnPreviousReleaseNotes(gitRepo, previousReleaseNotes) ?? tagToStartFrom.Commit.Author.When; var filter = new IssueTrackerFilter { Since = findIssuesSince, IncludeOpen = false }; var closedIssues = (await issueTracker.GetIssuesAsync(filter)).ToArray(); // As discussed here: https://github.com/GitTools/GitReleaseNotes/issues/85 var semanticReleases = new Dictionary<string, SemanticRelease>(); foreach (var issue in closedIssues) { // 1) Include all issues from the issue tracker that are assigned to this release foreach (var fixVersion in issue.FixVersions) { if (!fixVersion.IsReleased) { continue; } if (!semanticReleases.ContainsKey(fixVersion.Name)) { semanticReleases.Add(fixVersion.Name, new SemanticRelease(fixVersion.Name, fixVersion.ReleaseDate)); } var semanticRelease = semanticReleases[fixVersion.Name]; var releaseNoteItem = new ReleaseNoteItem(issue.Title, issue.Id, issue.Url, issue.Labels, issue.DateClosed, new Contributor[] { /*TODO: implement*/ }); semanticRelease.ReleaseNoteLines.Add(releaseNoteItem); } // 2) Get closed issues from the issue tracker that have no fixversion but are closed between the last release and this release if (issue.FixVersions.Count == 0) { foreach (var release in releases) { if (issue.DateClosed.HasValue && issue.DateClosed.Value > release.PreviousReleaseDate && (release.When == null || issue.DateClosed <= release.When)) { if (!semanticReleases.ContainsKey(release.Name)) { var beginningSha = release.FirstCommit != null ? release.FirstCommit.Substring(0, 10) : null; var endSha = release.LastCommit != null ? release.LastCommit.Substring(0, 10) : null; semanticReleases.Add(release.Name, new SemanticRelease(release.Name, release.When, new ReleaseDiffInfo { BeginningSha = beginningSha, EndSha = endSha, // TODO DiffUrlFormat = context.Repository.DiffUrlFormat })); } var semanticRelease = semanticReleases[release.Name]; var releaseNoteItem = new ReleaseNoteItem(issue.Title, issue.Id, issue.Url, issue.Labels, issue.DateClosed, issue.Contributors); semanticRelease.ReleaseNoteLines.Add(releaseNoteItem); } } } } // 3) Remove any duplicates foreach (var semanticRelease in semanticReleases.Values) { var handledIssues = new HashSet<string>(); for (var i = 0; i < semanticRelease.ReleaseNoteLines.Count; i++) { var releaseNoteLine = semanticRelease.ReleaseNoteLines[i] as ReleaseNoteItem; if (releaseNoteLine == null) { continue; } if (handledIssues.Contains(releaseNoteLine.IssueNumber)) { semanticRelease.ReleaseNoteLines.RemoveAt(i--); continue; } handledIssues.Add(releaseNoteLine.IssueNumber); } } var semanticReleaseNotes = new SemanticReleaseNotes(semanticReleases.Values, categories); var mergedReleaseNotes = semanticReleaseNotes.Merge(previousReleaseNotes); return mergedReleaseNotes; }