public void ShouldWriteAllVersionsToProjectFiles() { var tempDir = TempDir.Create(); TempCsProject.Create(Path.Join(tempDir, "project1"), "1.1.1"); TempCsProject.Create(Path.Join(tempDir, "project2"), "1.1.1"); var projects = Projects.Discover(tempDir); projects.WriteVersion(new Version(2, 0, 0)); var updated = Projects.Discover(tempDir); updated.Version.ShouldBe(Version.Parse("2.0.0")); }
public void WriteVersion(Version nextVersion) { var doc = new XmlDocument { PreserveWhitespace = true }; try { doc.Load(ProjectFile); } catch (Exception) { throw new InvalidOperationException($"Project {ProjectFile} is not a valid csproj file. Please make sure that you have a valid csproj file in place!"); } var versionElement = doc.SelectSingleNode("/Project/PropertyGroup/Version"); versionElement.InnerText = nextVersion.ToString(); doc.Save(ProjectFile); }
public Version NextVersion(Version version, bool ignoreInsignificant = false) { switch (_versionImpact) { case VersionImpact.Patch: return(new Version(version.Major, version.Minor, version.Patch + 1)); case VersionImpact.Minor: return(new Version(version.Major, version.Minor + 1, 0)); case VersionImpact.Major: return(new Version(version.Major + 1, 0, 0)); case VersionImpact.None: var buildVersion = ignoreInsignificant ? version.Patch : version.Patch + 1; return(new Version(version.Major, version.Minor, buildVersion)); default: throw new InvalidOperationException($"Version impact of {_versionImpact} cannot be handled"); } }
public string BuildVersionTagLink(Version version) { return($"https://{_organization}@dev.azure.com/{_organization}/{_repository}/releases/tag/v{version}"); }
public static Tag SelectVersionTag(this Repository repository, Version version) { return(repository.Tags.SingleOrDefault(t => t.FriendlyName == $"v{version}")); }
public Version Versionize(bool dryrun = false, bool skipDirtyCheck = false, bool skipCommit = false, string releaseVersion = null, bool ignoreInsignificant = false, bool includeAllCommitsInChangelog = false, string releaseCommitMessageSuffix = null) { var workingDirectory = _directory.FullName; using (var repo = new Repository(workingDirectory)) { var isDirty = repo.RetrieveStatus(new StatusOptions()).IsDirty; if (!skipDirtyCheck && isDirty) { Exit($"Repository {workingDirectory} is dirty. Please commit your changes.", 1); } var projects = Projects.Discover(workingDirectory); if (projects.IsEmpty()) { Exit($"Could not find any projects files in {workingDirectory} that have a <Version> defined in their csproj file.", 1); } if (projects.HasInconsistentVersioning()) { Exit($"Some projects in {workingDirectory} have an inconsistent <Version> defined in their csproj file. Please update all versions to be consistent or remove the <Version> elements from projects that should not be versioned", 1); } Information($"Discovered {projects.GetProjectFiles().Count()} versionable projects"); foreach (var project in projects.GetProjectFiles()) { Information($" * {project}"); } var versionTag = repo.SelectVersionTag(projects.Version); var commitsInVersion = repo.GetCommitsSinceLastVersion(versionTag); var conventionalCommits = ConventionalCommitParser.Parse(commitsInVersion); var versionIncrement = VersionIncrementStrategy.CreateFrom(conventionalCommits); var nextVersion = versionTag == null ? projects.Version : versionIncrement.NextVersion(projects.Version, ignoreInsignificant); if (ignoreInsignificant && nextVersion == projects.Version) { Exit($"Version was not affected by commits since last release ({projects.Version}), since you specified to ignore insignificant changes, no action will be performed.", 0); } if (!string.IsNullOrWhiteSpace(releaseVersion)) { try { nextVersion = Version.Parse(releaseVersion); } catch (Exception) { Exit($"Could not parse the specified release version {releaseVersion} as valid version", 1); } } var versionTime = DateTimeOffset.Now; // Commit changelog and version source if (!dryrun && (nextVersion != projects.Version)) { projects.WriteVersion(nextVersion); foreach (var projectFile in projects.GetProjectFiles()) { Commands.Stage(repo, projectFile); } } Step($"bumping version from {projects.Version} to {nextVersion} in projects"); var changelog = Changelog.Discover(workingDirectory); if (!dryrun) { var changelogLinkBuilder = ChangelogLinkBuilderFactory.CreateFor(repo); changelog.Write(nextVersion, versionTime, changelogLinkBuilder, conventionalCommits, includeAllCommitsInChangelog); } Step("updated CHANGELOG.md"); if (!dryrun && !skipCommit) { Commands.Stage(repo, changelog.FilePath); foreach (var projectFile in projects.GetProjectFiles()) { Commands.Stage(repo, projectFile); } } if (!dryrun && !skipCommit) { var author = repo.Config.BuildSignature(versionTime); var committer = author; // TODO: Check if tag exists before commit var releaseCommitMessage = $"chore(release): {nextVersion} {releaseCommitMessageSuffix}".TrimEnd(); var versionCommit = repo.Commit(releaseCommitMessage, author, committer); Step("committed changes in projects and CHANGELOG.md"); repo.Tags.Add($"v{nextVersion}", versionCommit, author, $"{nextVersion}"); Step($"tagged release as {nextVersion}"); Information(""); Information("i Run `git push --follow-tags origin master` to push all changes including tags"); } else if (skipCommit) { Information(""); Information($"i Commit and tagging of release was skipped. Tag this release as `v{nextVersion}` to make versionize detect the release"); } return(nextVersion); } }
private Project(string projectFile, Version version) { ProjectFile = projectFile; Version = version; }
public void Write(Version version, DateTimeOffset versionTime, IChangelogLinkBuilder linkBuilder, IEnumerable <ConventionalCommit> commits, bool includeAllCommitsInChangelog = false) { var versionTagLink = string.IsNullOrWhiteSpace(linkBuilder.BuildVersionTagLink(version)) ? version.ToString() : $"[{version}]({linkBuilder.BuildVersionTagLink(version)})"; var markdown = $"<a name=\"{version}\"></a>"; markdown += "\n"; markdown += $"## {versionTagLink} ({versionTime.Year}-{versionTime.Month}-{versionTime.Day})"; markdown += "\n"; markdown += "\n"; var bugFixes = BuildBlock("Bug Fixes", linkBuilder, commits.Where(commit => commit.IsFix)); if (!string.IsNullOrWhiteSpace(bugFixes)) { markdown += bugFixes; markdown += "\n"; } var features = BuildBlock("Features", linkBuilder, commits.Where(commit => commit.IsFeature)); if (!string.IsNullOrWhiteSpace(features)) { markdown += features; markdown += "\n"; } var breaking = BuildBlock("Breaking Changes", linkBuilder, commits.Where(commit => commit.IsBreakingChange)); if (!string.IsNullOrWhiteSpace(breaking)) { markdown += breaking; markdown += "\n"; } if (includeAllCommitsInChangelog) { var other = BuildBlock("Other", linkBuilder, commits.Where(commit => !commit.IsFix && !commit.IsFeature && !commit.IsBreakingChange)); if (!string.IsNullOrWhiteSpace(other)) { markdown += other; markdown += "\n"; } } if (File.Exists(FilePath)) { var contents = File.ReadAllText(FilePath); var firstReleaseHeadlineIdx = contents.IndexOf("<a name=\"", StringComparison.Ordinal); if (firstReleaseHeadlineIdx >= 0) { markdown = contents.Insert(firstReleaseHeadlineIdx, markdown); } else { markdown = contents + "\n\n" + markdown; } File.WriteAllText(FilePath, markdown); } else { File.WriteAllText(FilePath, Preamble + "\n" + markdown); } }