├── .gitignore ├── .travis.yml ├── LICENSE ├── NuGet.config ├── build ├── build.sh └── test.sh ├── readme.md └── src ├── SemanticRelease.CommitAnalyzer.Tests ├── CommitMessageParserTests.cs ├── FileSystemGitRepositoryTests.cs ├── InMemoryGitRepositoryTests.cs ├── ProjectReleaserTests.cs ├── SemanticRelease.CommitAnalyzer.Tests.csproj └── VersionCalculatorTests.cs ├── SemanticRelease.CommitAnalyzer ├── CommitMessageParser.cs ├── DefaultCommitAnalyzer.cs ├── DefaultPreConditions.cs ├── DotnetCoreBuildTools.cs ├── DotnetProjectParser.cs ├── GitHubSourcePublisher.cs ├── InMemoryGitRepository.cs ├── OnDiskGitRepository.cs ├── ProjectReleaser.cs ├── SemanticRelease.CommitAnalyzer.csproj └── VersionCalculator.cs ├── SemanticRelease.Core ├── Program.cs ├── SemanticRelease.Core.csproj └── cli │ ├── CurrentVersionCli.cs │ ├── PublishCli.cs │ ├── ReleaseCli.cs │ ├── SemanticReleaseEntry.cs │ └── ToolCliBase.cs ├── SemanticRelease.Extensibility ├── CommitStatusEventArgs.cs ├── ICommitAnalyzer.cs ├── IPostReleaseAction.cs ├── IPreConditionVerifier.cs ├── IProjectManager.cs ├── IProjectReleaseStrategy.cs ├── IReleaseVerifier.cs ├── ISourcePublisher.cs ├── ISourceRepositoryProvider.cs ├── IVersionCalculator.cs ├── Model │ ├── NoOpReleaseException.cs │ ├── Release.cs │ ├── ReleaseCommit.cs │ ├── ReleaseRepository.cs │ └── SemanticReleaseVersion.cs ├── ReleaseType.cs └── SemanticRelease.Extensibility.csproj ├── SemanticRelease.GlobalTool ├── Program.cs └── SemanticRelease.GlobalTool.csproj ├── SemanticRelease.Tool ├── Program.cs └── SemanticRelease.Tool.csproj └── dotnet-semantic-release.sln /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.*~ 3 | project.lock.json 4 | .DS_Store 5 | *.pyc 6 | 7 | # Visual Studio Code 8 | .vscode 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | [Pp]ackages/ 22 | [Rr]eports/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | msbuild.log 29 | msbuild.err 30 | msbuild.wrn 31 | coverage.info 32 | coverage.xml 33 | 34 | # Visual Studio 2015 35 | .vs/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: src/dotnet-semantic-release.sln 3 | mono: none 4 | dotnet: 2.1.500 5 | install: 6 | - mkdir packages 7 | - dotnet restore src 8 | script: 9 | - ./build/test.sh $(pwd) 10 | - ./build/build.sh $(pwd) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 axlj45 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKING_DIR=$1 4 | SRC_DIR=$WORKING_DIR/src 5 | PKG_DIR=$WORKING_DIR/packages 6 | 7 | mkdir -p $PKG_DIR 8 | rm -rf $PKG_DIR/*.nupkg 9 | 10 | dotnet pack --configuration=release $SRC_DIR/SemanticRelease.Extensibility/*.csproj 11 | dotnet pack --configuration=release $SRC_DIR/SemanticRelease.CommitAnalyzer/*.csproj 12 | dotnet pack --configuration=release $SRC_DIR/SemanticRelease.Core/*.csproj 13 | dotnet build --configuration=release $SRC_DIR/SemanticRelease.Tool/*.csproj 14 | cp $SRC_DIR/**/bin/release/*.nupkg $PKG_DIR 15 | 16 | dotnet build --configuration=release $SRC_DIR/SemanticRelease.GlobalTool/*.csproj 17 | cp $SRC_DIR/SemanticRelease.GlobalTool/bin/release/*.nupkg $PKG_DIR -------------------------------------------------------------------------------- /build/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKING_DIR=$1 4 | 5 | for project in $(find $WORKING_DIR -name *.Tests.csproj); do 6 | dotnet test $project /p:CollectCoverage=true /p:CoverletOutputFormat=lcov; 7 | done -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Dotnet Semantic Release 2 | 3 | Inspired by the [Semantic Release][0] module for NodeJS, the intent is to completely remove manual intervention from the versioning process by automating the [Semantic Versioning][1] specification during build/release time. 4 | 5 | **Note**: This package is still in a very early phase of development. Pull requests are welcome. 6 | 7 | [![Build Status](https://travis-ci.org/axlj45/dotnet-semantic-release.svg?branch=trunk)](https://travis-ci.org/axlj45/dotnet-semantic-release) 8 | 9 | ## Usage 10 | 11 | ### Global Tool 12 | 13 | ```sh 14 | # Install tool 15 | dotnet tool install --global SemanticRelease.GlobalTool 16 | 17 | # View help 18 | semantic-release --help 19 | 20 | # Version and tag application 21 | semantic-release release --project-path 22 | ``` 23 | 24 | ### Dotnet CLI Tool 25 | 26 | Add the following to your `.csproj` file: 27 | 28 | ```xml 29 | 30 | 31 | 32 | ``` 33 | 34 | ```sh 35 | # Navigate to the *.csproj location 36 | cd 37 | 38 | # Version and tag application 39 | dotnet-semanticrelease release 40 | ``` 41 | 42 | ### Standardizing commit messages 43 | 44 | [Commitzen][2] is a great NodeJS tool that prompts developers at the time of commit for information regarding commit scope, description, issue id, etc. It uses this information to form a commit message that follows a consistent format and makes it easy for tools like this to intepret. 45 | 46 | dotnet-semantic-release currently relies on the `cz-convential-changelog` library for parsing commit messages. 47 | 48 | To use it, install the latest version of NodeJS, NPM, and Conventional changelog library. 49 | 50 | ```sh 51 | # Install commitizen 52 | npm install -g commitizen 53 | 54 | # Install conventional-changelog library 55 | npm install -g cz-conventional-changelog 56 | 57 | # Set the default changelog library 58 | echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc 59 | ``` 60 | 61 | From here on out, execute `npx git-cz` to perform commits. 62 | 63 | ## How does it work? 64 | 65 | The tool checks for keywords inside commit messages to identify if the changes that occured should cause a version bump. 66 | 67 | For example: 68 | 69 | * `feat`: will bump the *minor* version number (ie. 1.**4**.3 would become 1.**5**.0). 70 | * `fix`: will bump the patch version (ie. 1.27.**0** becomes 1.27.**1**). 71 | * `Breaking Change`: will cause the major version to increment (ie. **2**.4.1 would become **3**.0.0). 72 | 73 | 74 | ## What happens when I "release"? 75 | 76 | * Locate the nearest `.csproj` file and read the `Version`. If `Version` doesn't exis, try to read `PackageVersion`. 77 | * Verify that the current branch is the configured release branch, it has a remote source, and the current branch is up to date with the remote source 78 | * Calculate the next version 79 | * Update the `Version` and `PackageVersion` (if applicable) 80 | * Tag the current branch 81 | 82 | ## Limitations 83 | 84 | Here are a list of current limitations that will be fixed/implemented in future release of the library. The list is in no particular order. 85 | 86 | * Automatic changelog generation not implemented yet 87 | * Extensibility model is unstable 88 | * Only GIT is supported 89 | * Tags are not automatically committed 90 | * `Version` and `PackageVersion` are considered synonymous when locating an existing version. 91 | * `VersionPrefix` and `VersionSuffix` are not considered 92 | 93 | 94 | [0]:https://github.com/semantic-release/semantic-release 95 | [1]:https://semver.org/ 96 | [2]:https://github.com/commitizen/cz-cli -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer.Tests/CommitMessageParserTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using SemanticRelease.Extensibility; 4 | using SemanticRelease.Extensibility.Model; 5 | using Xunit; 6 | 7 | namespace SemanticRelease.CommitAnalyzer.Tests 8 | { 9 | public class CommitMessageParserTests 10 | { 11 | [Fact] 12 | public void NonVersionableChangesYieldsNoRelease() 13 | { 14 | var commits = new List(); 15 | commits.Add(GetNonVersionableChange()); 16 | 17 | var parser = new CommitMessageParser(commits); 18 | var result = parser.GetReleaseType(); 19 | 20 | Assert.Equal(ReleaseType.NONE, result); 21 | } 22 | 23 | [Fact] 24 | public void FixYieldsPatchRelease() 25 | { 26 | var commits = new List(); 27 | commits.Add(GetPatchCommit()); 28 | 29 | var parser = new CommitMessageParser(commits); 30 | var result = parser.GetReleaseType(); 31 | 32 | Assert.Equal(ReleaseType.PATCH, result); 33 | } 34 | 35 | [Fact] 36 | public void PeformanceYieldsPatchRelease() 37 | { 38 | var commits = new List(); 39 | commits.Add(GetCommit("perf")); 40 | 41 | var parser = new CommitMessageParser(commits); 42 | var result = parser.GetReleaseType(); 43 | 44 | Assert.Equal(ReleaseType.PATCH, result); 45 | } 46 | 47 | [Fact] 48 | public void SecurityYieldsPatchRelease() 49 | { 50 | var commits = new List(); 51 | commits.Add(GetCommit("security")); 52 | 53 | var parser = new CommitMessageParser(commits); 54 | var result = parser.GetReleaseType(); 55 | 56 | Assert.Equal(ReleaseType.PATCH, result); 57 | } 58 | 59 | [Fact] 60 | public void FeatureYieldsMinorRelease() 61 | { 62 | var commits = new List(); 63 | commits.Add(GetFeatureCommit()); 64 | 65 | var parser = new CommitMessageParser(commits); 66 | var result = parser.GetReleaseType(); 67 | 68 | Assert.Equal(ReleaseType.MINOR, result); 69 | } 70 | 71 | [Fact] 72 | public void BreakingChangeYieldsMajorRelease() 73 | { 74 | var commits = new List(); 75 | commits.Add(GetBreakingPatchCommit()); 76 | 77 | var parser = new CommitMessageParser(commits); 78 | var result = parser.GetReleaseType(); 79 | 80 | Assert.Equal(ReleaseType.MAJOR, result); 81 | } 82 | 83 | [Fact] 84 | public void MinorReleaseCommitTakesPrecedenceOverPatch() 85 | { 86 | var commits = new List(); 87 | commits.Add(GetFeatureCommit()); 88 | commits.Add(GetPatchCommit()); 89 | 90 | var parser = new CommitMessageParser(commits); 91 | var result = parser.GetReleaseType(); 92 | 93 | Assert.Equal(ReleaseType.MINOR, result); 94 | } 95 | 96 | [Fact] 97 | public void MajorReleaseCommitTakesPrecedenceOverMinor() 98 | { 99 | var commits = new List(); 100 | commits.Add(GetFeatureCommit()); 101 | commits.Add(GetBreakingFeatureCommit()); 102 | 103 | var parser = new CommitMessageParser(commits); 104 | var result = parser.GetReleaseType(); 105 | 106 | Assert.Equal(ReleaseType.MAJOR, result); 107 | } 108 | 109 | 110 | 111 | private ReleaseCommit GetPatchCommit() 112 | { 113 | return GetCommit("fix"); 114 | } 115 | 116 | private ReleaseCommit GetFeatureCommit() 117 | { 118 | return GetCommit("feat"); 119 | } 120 | 121 | private ReleaseCommit GetBreakingFeatureCommit() 122 | { 123 | return GetCommit("feat", true); 124 | } 125 | 126 | private ReleaseCommit GetBreakingPatchCommit() 127 | { 128 | return GetCommit("fix", true); 129 | } 130 | 131 | private ReleaseCommit GetNonVersionableChange() 132 | { 133 | return GetCommit("chore"); 134 | } 135 | 136 | private ReleaseCommit GetNonVersionableBreakingChange() 137 | { 138 | return GetCommit("chore", true); 139 | } 140 | 141 | private ReleaseCommit GetCommit(string commitType, bool isBreaking = false, string msg = "Standard message") 142 | { 143 | string commitMsg = $"{commitType}: ${msg}"; 144 | 145 | if (isBreaking) 146 | { 147 | commitMsg += Environment.NewLine; 148 | commitMsg += "BREAKING: A breaking change occurred."; 149 | } 150 | 151 | return new ReleaseCommit(0, "DEADBEEF", commitMsg); 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer.Tests/FileSystemGitRepositoryTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using System.IO.Abstractions.TestingHelpers; 3 | 4 | namespace SemanticRelease.CommitAnalyzer.Tests 5 | { 6 | public class FileSystemGitRepositoryTests 7 | { 8 | [Fact] 9 | public void CannotInitializeBadFileSystemGitRepository() 10 | { 11 | var emptyFileSystem = new MockFileSystem(); 12 | Assert.Throws(() => new OnDiskGitRepository("trunk", "./", emptyFileSystem)); 13 | } 14 | 15 | [Fact(Skip = "Not ready yet...")] 16 | public void CanInitializeFileSystemGitRepository() 17 | { 18 | var repository = new OnDiskGitRepository("trunk", "./", new MockFileSystem()); 19 | 20 | Assert.Equal("InMemory", repository.RepositoryPath); 21 | Assert.Equal("trunk", repository.ReleaseBranch); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer.Tests/InMemoryGitRepositoryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace SemanticRelease.CommitAnalyzer.Tests 5 | { 6 | public class InMemoryGitRepositoryTests 7 | { 8 | [Fact] 9 | public void CanInitializeInMemoryGitRepository() 10 | { 11 | var repository = new InMemoryGitRepository("trunk"); 12 | 13 | Assert.Equal("InMemory", repository.RepositoryPath); 14 | Assert.Equal("trunk", repository.ReleaseBranch); 15 | Assert.NotNull(repository.RepositoryRef); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer.Tests/ProjectReleaserTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Abstractions.TestingHelpers; 2 | using LibGit2Sharp; 3 | using Moq; 4 | using SemanticRelease.Extensibility; 5 | using Xunit; 6 | 7 | namespace SemanticRelease.CommitAnalyzer.Tests 8 | { 9 | public class ProjectReleaserTests 10 | { 11 | private MockFileSystem _fileSystem { get; } 12 | 13 | public ProjectReleaserTests() 14 | { 15 | _fileSystem = new MockFileSystem(); 16 | _fileSystem.AddDirectory("/home/projects/semantic_release/src"); 17 | } 18 | 19 | [Fact] 20 | public void ProjectCanRelease() 21 | { 22 | string repositoryPath = "/home/projects/semantic_release"; 23 | string projectPath = "/home/projects/semantic_release/src"; 24 | 25 | var projectManager = new Mock(); 26 | projectManager.SetupGet(o => o.ProjectPath).Returns(projectPath); 27 | 28 | var repo = new Mock>(); 29 | repo.SetupGet(o => o.RepositoryPath).Returns(repositoryPath); 30 | 31 | var repository = new Repository(); 32 | 33 | repo.SetupGet(o => o.RepositoryRef).Returns(repository); 34 | 35 | var releaser = new ProjectReleaser(projectManager.Object, repo.Object, _fileSystem); 36 | 37 | // releaser.PrepareForRelease(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer.Tests/SemanticRelease.CommitAnalyzer.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | false 6 | A. Leonard 7 | 8 | 9 | 10 | 11 | 12 | runtime; build; native; contentfiles; analyzers 13 | all 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer.Tests/VersionCalculatorTests.cs: -------------------------------------------------------------------------------- 1 | using SemanticRelease.Extensibility; 2 | using SemanticRelease.Extensibility.Model; 3 | using Xunit; 4 | 5 | namespace SemanticRelease.CommitAnalyzer.Tests 6 | { 7 | public class VersionCalculatorTests 8 | { 9 | IVersionCalculator systemUnderTest = new VersionCalculator(); 10 | 11 | [Fact] 12 | public void MajorReleaseShouldIncrementMajorVersion() 13 | { 14 | var lastRelease = new Release("1.0.0", "DEADBEEF"); 15 | 16 | var nextVersion = systemUnderTest.GetNextVersion(lastRelease, ReleaseType.MAJOR); 17 | 18 | Assert.Equal("2.0.0", nextVersion.ToString()); 19 | } 20 | 21 | [Fact] 22 | public void MinorReleaseShouldIncrementMinorVersion() 23 | { 24 | var lastRelease = new Release("1.0.0", "DEADBEEF"); 25 | 26 | var nextVersion = systemUnderTest.GetNextVersion(lastRelease, ReleaseType.MINOR); 27 | 28 | Assert.Equal("1.1.0", nextVersion.ToString()); 29 | } 30 | 31 | 32 | [Fact] 33 | public void PatchReleaseShouldIncrementPatchVersion() 34 | { 35 | var lastRelease = new Release("1.0.0", "DEADBEEF"); 36 | 37 | var nextVersion = systemUnderTest.GetNextVersion(lastRelease, ReleaseType.PATCH); 38 | 39 | Assert.Equal("1.0.1", nextVersion.ToString()); 40 | } 41 | 42 | [Fact] 43 | public void NoReleaseShouldThrowExeption() 44 | { 45 | var lastRelease = new Release("1.0.0", "DEADBEEF"); 46 | 47 | Assert.Throws(() => systemUnderTest.GetNextVersion(lastRelease, ReleaseType.NONE)); 48 | } 49 | 50 | [Fact] 51 | public void VersionShouldDefaultToOneO() 52 | { 53 | Release lastRelease = null; 54 | 55 | var nextVersion = systemUnderTest.GetNextVersion(lastRelease, ReleaseType.MAJOR); 56 | 57 | Assert.Equal("1.0.0", nextVersion.ToString()); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/CommitMessageParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.CompilerServices; 3 | using System.Text.RegularExpressions; 4 | using SemanticRelease.Extensibility; 5 | using SemanticRelease.Extensibility.Model; 6 | 7 | [assembly: InternalsVisibleTo("SemanticRelease.CommitAnalyzer.Tests")] 8 | namespace SemanticRelease.CommitAnalyzer 9 | { 10 | internal class CommitMessageParser 11 | { 12 | private readonly IEnumerable _commitsSinceRelease; 13 | 14 | public CommitMessageParser(IEnumerable commitsSinceRelease) 15 | { 16 | _commitsSinceRelease = commitsSinceRelease; 17 | } 18 | 19 | public ReleaseType GetReleaseType() 20 | { 21 | var releaseType = ReleaseType.NONE; 22 | 23 | var multiLineIgnoreCase = RegexOptions.IgnoreCase | RegexOptions.Singleline; 24 | 25 | var majorRelease = new Regex("(BREAKING)", RegexOptions.Singleline); 26 | var minorRelease = new Regex(@"(feat:|feature:|feat\(.*\))", multiLineIgnoreCase); 27 | var patchRelease = new Regex(@"(fix|perf|security)(\(.*\))?:", multiLineIgnoreCase); 28 | 29 | foreach (var commit in _commitsSinceRelease) 30 | { 31 | if (majorRelease.IsMatch(commit.Message)) 32 | { 33 | releaseType = ReleaseType.MAJOR; 34 | break; 35 | } 36 | 37 | if (minorRelease.IsMatch(commit.Message) || releaseType == ReleaseType.MINOR) 38 | { 39 | releaseType = ReleaseType.MINOR; 40 | continue; 41 | } 42 | 43 | if (patchRelease.IsMatch(commit.Message)) 44 | { 45 | releaseType = ReleaseType.PATCH; 46 | } 47 | } 48 | 49 | return releaseType; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/DefaultCommitAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using SemanticRelease.Extensibility; 5 | using SemanticRelease.Extensibility.Model; 6 | using LibGit2Sharp; 7 | using SemanticVersion = SemVer.Version; 8 | 9 | namespace SemanticRelease.CommitAnalyzer 10 | { 11 | public class DefaultCommitAnalyzer : ICommitAnalyzer 12 | { 13 | private ISourceRepositoryProvider _repository; 14 | private readonly IRepository _repoReference; 15 | private readonly VersionCalculator _versionCalculator; 16 | 17 | public event EventHandler CommitEvent; 18 | 19 | public DefaultCommitAnalyzer(ISourceRepositoryProvider repositoryProvider) 20 | { 21 | this._repository = repositoryProvider; 22 | 23 | var repoRef = _repository.RepositoryRef as ReleaseRepository; 24 | this._repoReference = repoRef.GetRepositoryReference(); 25 | 26 | this._versionCalculator = new VersionCalculator(); 27 | } 28 | 29 | public Release CalculateNextRelease() 30 | { 31 | var lastRelease = GetLastRelease(); 32 | 33 | var msg = "This is the first release."; 34 | 35 | SendEvent($"Last Release: {lastRelease?.Version.ToString() ?? msg}"); 36 | 37 | var commitsSinceRelease = CommitsSinceLastRelease(lastRelease).ToList(); 38 | 39 | var releaseType = new CommitMessageParser(commitsSinceRelease).GetReleaseType(); 40 | 41 | SendEvent($"Release type: {releaseType}"); 42 | 43 | var nextVersion = _versionCalculator.GetNextVersion(lastRelease, releaseType); 44 | 45 | SendEvent($"Next version: {nextVersion}"); 46 | 47 | return new Release(nextVersion.ToString(), null); 48 | } 49 | 50 | private void SendEvent(string message) 51 | { 52 | CommitEvent?.Invoke(this, new CommitStatusEventArgs(message)); 53 | } 54 | 55 | private IEnumerable CommitsSinceLastRelease(Release lastRelease) 56 | { 57 | var lastReleaseCommit = _repoReference.Commits.FirstOrDefault(o => o.Sha.Equals(lastRelease?.Sha)); 58 | int index = 0; 59 | 60 | foreach (var commit in _repoReference.Commits) 61 | { 62 | if (commit.Sha.Equals(lastReleaseCommit?.Sha)) break; 63 | 64 | yield return new ReleaseCommit(index++, commit.Sha, commit.Message); 65 | } 66 | } 67 | 68 | public Release GetLastRelease() 69 | { 70 | var lastRelease = _repoReference.Tags.Where(o => 71 | { 72 | try 73 | { 74 | var version = new SemanticVersion(o.FriendlyName); 75 | return string.IsNullOrEmpty(version.PreRelease); 76 | } 77 | catch 78 | { 79 | return false; 80 | } 81 | }) 82 | .Select(o => new { Commit = o, Version = new SemanticVersion(o.FriendlyName) }) 83 | .OrderByDescending(o => o.Version) 84 | .Select(o => new Release(o.Version.ToString(), o.Commit.PeeledTarget.Sha)) 85 | .FirstOrDefault(); 86 | 87 | return lastRelease; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/DefaultPreConditions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SemanticRelease.Extensibility; 3 | using LibGit2Sharp; 4 | using SemanticRelease.Extensibility.Model; 5 | 6 | namespace SemanticRelease.CommitAnalyzer 7 | { 8 | public class DefaultPreConditions : IPreConditionVerifier 9 | { 10 | private readonly ISourceRepositoryProvider _repository; 11 | 12 | public DefaultPreConditions(ISourceRepositoryProvider repositoryProvider) 13 | { 14 | this._repository = repositoryProvider; 15 | } 16 | 17 | public void Verify(bool detachedHead) 18 | { 19 | var repoRef = _repository.RepositoryRef as ReleaseRepository; 20 | 21 | var repo = repoRef.GetRepositoryReference(); 22 | 23 | if (!detachedHead && !repo.Head.FriendlyName.Equals(_repository.ReleaseBranch)) 24 | { 25 | throw new Exception($"Wrong Branch: {repo.Head.FriendlyName} must be {_repository.ReleaseBranch} to deploy"); 26 | } 27 | 28 | if (!detachedHead && !repo.Head.IsTracking) 29 | { 30 | throw new Exception("No remote found for current branch."); 31 | } 32 | 33 | if (!repo.Head.IsCurrentRepositoryHead) 34 | { 35 | throw new Exception("Local repository is not in sync with remote repository."); 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/DotnetCoreBuildTools.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using Microsoft.Build.Evaluation; 6 | using Microsoft.Build.Utilities; 7 | 8 | namespace SemanticRelease.CommitAnalyzer 9 | { 10 | // Pulled from https://daveaglick.com/posts/running-a-design-time-build-with-msbuild-apis 11 | public static class DotnetCoreBuildTools 12 | { 13 | public static string GetCoreBasePath(string projectPath) 14 | { 15 | // Ensure that we set the DOTNET_CLI_UI_LANGUAGE environment variable to "en-US" before 16 | // running 'dotnet --info'. Otherwise, we may get localized results. 17 | 18 | string originalCliLanguage = Environment.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"); 19 | Environment.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US"); 20 | 21 | try 22 | { 23 | // Create the process info 24 | ProcessStartInfo startInfo = new ProcessStartInfo("dotnet", "--info") 25 | { 26 | // global.json may change the version, so need to set working directory 27 | WorkingDirectory = Path.GetDirectoryName(projectPath), 28 | CreateNoWindow = true, 29 | UseShellExecute = false, 30 | RedirectStandardOutput = true, 31 | RedirectStandardError = true 32 | }; 33 | 34 | // Execute the process 35 | using (Process process = Process.Start(startInfo)) 36 | { 37 | List lines = new List(); 38 | process.OutputDataReceived += (_, e) => 39 | { 40 | if (!string.IsNullOrWhiteSpace(e.Data)) 41 | { 42 | lines.Add(e.Data); 43 | } 44 | }; 45 | process.BeginOutputReadLine(); 46 | process.WaitForExit(); 47 | return ParseCoreBasePath(lines); 48 | } 49 | } 50 | finally 51 | { 52 | Environment.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", originalCliLanguage); 53 | } 54 | } 55 | 56 | public static string ParseCoreBasePath(List lines) 57 | { 58 | if (lines == null || lines.Count == 0) 59 | { 60 | throw new Exception("Could not get results from `dotnet --info` call"); 61 | } 62 | 63 | foreach (string line in lines) 64 | { 65 | int colonIndex = line.IndexOf(':'); 66 | if (colonIndex >= 0 67 | && line.Substring(0, colonIndex).Trim().Equals("Base Path", StringComparison.OrdinalIgnoreCase)) 68 | { 69 | return line.Substring(colonIndex + 1).Trim(); 70 | } 71 | } 72 | 73 | throw new Exception("Could not locate base path in `dotnet --info` results"); 74 | } 75 | 76 | public static Dictionary GetCoreGlobalProperties(string projectPath, string toolsPath) 77 | { 78 | string solutionDir = Path.GetDirectoryName(projectPath); 79 | string extensionsPath = toolsPath; 80 | string sdksPath = Path.Combine(toolsPath, "Sdks"); 81 | string roslynTargetsPath = Path.Combine(toolsPath, "Roslyn"); 82 | 83 | return new Dictionary 84 | { 85 | { "SolutionDir", solutionDir }, 86 | { "MSBuildExtensionsPath", extensionsPath }, 87 | { "MSBuildSDKsPath", sdksPath }, 88 | { "RoslynTargetsPath", roslynTargetsPath } 89 | }; 90 | } 91 | 92 | 93 | public static Project GetCoreProject(string projectPath) 94 | { 95 | string toolsPath = GetCoreBasePath(projectPath); 96 | Dictionary globalProperties = GetCoreGlobalProperties(projectPath, toolsPath); 97 | ProjectCollection projectCollection = new ProjectCollection(globalProperties); 98 | projectCollection.AddToolset(new Toolset(ToolLocationHelper.CurrentToolsVersion, toolsPath, projectCollection, string.Empty)); 99 | 100 | Environment.SetEnvironmentVariable("MSBuildExtensionsPath", globalProperties["MSBuildExtensionsPath"]); 101 | Environment.SetEnvironmentVariable("MSBuildSDKsPath", globalProperties["MSBuildSDKsPath"]); 102 | 103 | Project project = projectCollection.LoadProject(projectPath); 104 | return project; 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/DotnetProjectParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Abstractions; 3 | using System.Linq; 4 | using Microsoft.Build.Evaluation; 5 | using SemanticRelease.Extensibility; 6 | 7 | namespace SemanticRelease.CommitAnalyzer 8 | { 9 | public class DotnetProjectParser : IProjectManager 10 | { 11 | public string ProjectPath { get; } 12 | private readonly Project _project; 13 | private string _version; 14 | 15 | private PathBase Path { get; } 16 | private DirectoryBase Directory { get; } 17 | 18 | public DotnetProjectParser(string projectPath, IFileSystem fileSystem) 19 | { 20 | Path = fileSystem.Path; 21 | Directory = fileSystem.Directory; 22 | 23 | var workingDir = Path.GetDirectoryName(projectPath) + "/"; 24 | ProjectPath = FindProjPath(projectPath); 25 | _project = DotnetCoreBuildTools.GetCoreProject(ProjectPath); 26 | } 27 | 28 | public string GetVersion() 29 | { 30 | var props = _project.Xml.PropertyGroups.First(); // Need to make sure no other property groups exist with version in them. 31 | 32 | var version = props.Properties.Where(o => o.Name.Equals("Version")).FirstOrDefault(); 33 | var packageVer = props.Properties.FirstOrDefault(o => o.Name.Equals("PackageVersion")); 34 | 35 | return version?.Value ?? packageVer?.Value ?? string.Empty; 36 | } 37 | 38 | public void SetVersion(string version) 39 | { 40 | _version = version; 41 | 42 | var props = _project.Xml.PropertyGroups.First(); // Need to make sure no other property groups exist with version in them. 43 | props.SetProperty("Version", _version); 44 | 45 | var packageVer = props.Properties.FirstOrDefault(o => o.Name.Equals("PackageVersion")); 46 | if (!string.IsNullOrEmpty(packageVer?.Name)) props.SetProperty(packageVer.Name, _version); 47 | 48 | _project.Save(); 49 | } 50 | 51 | private string FindProjPath(string currentDir, int maxDepth = 3, int currentDepth = 0) 52 | { 53 | try 54 | { 55 | currentDepth++; 56 | 57 | if (currentDepth > maxDepth) throw new Exception(); 58 | 59 | var project = Directory.EnumerateFiles(currentDir, "*.csproj").FirstOrDefault(); 60 | 61 | if (string.IsNullOrEmpty(project)) 62 | return FindProjPath(Directory.GetParent(currentDir).FullName, maxDepth, currentDepth); 63 | else 64 | return project; 65 | } 66 | catch (Exception) 67 | { 68 | throw new Exception("Unable to locate dotnet project path."); 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/GitHubSourcePublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using LibGit2Sharp; 4 | using LibGit2Sharp.Handlers; 5 | using SemanticRelease.Extensibility; 6 | using SemanticRelease.Extensibility.Model; 7 | 8 | namespace SemanticRelease.CommitAnalyzer 9 | { 10 | public class GitHubSourcePublisher : ISourcePublisher 11 | { 12 | private readonly ReleaseRepository _repoRef; 13 | private readonly IRepository repo; 14 | 15 | public GitHubSourcePublisher(ISourceRepositoryProvider repositoryProvider) 16 | { 17 | _repoRef = repositoryProvider.RepositoryRef as ReleaseRepository; 18 | this.repo = _repoRef.GetRepositoryReference(); 19 | } 20 | 21 | public void Push() 22 | { 23 | var currentBranch = repo.Head.FriendlyName; 24 | var releaseBranch = _repoRef.ReleaseBranch; 25 | 26 | if (!string.Equals(releaseBranch, currentBranch)) 27 | throw new NoOpReleaseException($"Current branch '{currentBranch}' does not match release branch: {releaseBranch}"); 28 | 29 | var options = GetPushOptions(); 30 | repo.Network.Push(repo.Head, options); 31 | } 32 | 33 | public void Push(string tagToPush) 34 | { 35 | Push(); 36 | var options = GetPushOptions(); 37 | 38 | var selectedTag = repo 39 | .Tags 40 | .FirstOrDefault(o => o.FriendlyName.Equals(tagToPush, StringComparison.CurrentCultureIgnoreCase)) 41 | ?.CanonicalName; 42 | 43 | if (string.IsNullOrEmpty(selectedTag)) 44 | throw new Exception($"Cannot push {tagToPush}, tag not found."); 45 | 46 | var origin = repo.Network.Remotes["origin"]; 47 | 48 | repo.Network.Push(origin, selectedTag, options); 49 | } 50 | 51 | private PushOptions GetPushOptions() 52 | { 53 | var options = new PushOptions(); 54 | options.CredentialsProvider = GetCredentialsHandler(); 55 | return options; 56 | } 57 | 58 | private CredentialsHandler GetCredentialsHandler() 59 | { 60 | string user = Environment.GetEnvironmentVariable("GH_USER"); 61 | string token = Environment.GetEnvironmentVariable("GH_TOKEN"); 62 | var handler = new CredentialsHandler( 63 | (url, usernameFromUrl, types) => new UsernamePasswordCredentials() 64 | { 65 | Username = usernameFromUrl ?? user, 66 | Password = token 67 | }); 68 | return handler; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/InMemoryGitRepository.cs: -------------------------------------------------------------------------------- 1 | using LibGit2Sharp; 2 | using SemanticRelease.Extensibility; 3 | using SemanticRelease.Extensibility.Model; 4 | 5 | namespace SemanticRelease.CommitAnalyzer 6 | { 7 | public class InMemoryGitRepository : ISourceRepositoryProvider 8 | { 9 | private readonly ReleaseRepository _repository; 10 | 11 | public string ReleaseBranch => _repository.ReleaseBranch; 12 | 13 | public string RepositoryPath => _repository.RepositoryPath; 14 | 15 | public IRepository RepositoryRef => (IRepository)_repository.GetRepositoryReference(); 16 | 17 | object ISourceRepositoryProvider.RepositoryRef => _repository.GetRepositoryReference(); 18 | 19 | public InMemoryGitRepository(string releaseBranch) 20 | { 21 | var repoRef = new Repository(); 22 | 23 | _repository = new ReleaseRepository("InMemory", releaseBranch, repoRef); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/OnDiskGitRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Abstractions; 3 | using System.Linq; 4 | using LibGit2Sharp; 5 | using SemanticRelease.Extensibility; 6 | using SemanticRelease.Extensibility.Model; 7 | 8 | namespace SemanticRelease.CommitAnalyzer 9 | { 10 | public class OnDiskGitRepository : ISourceRepositoryProvider 11 | { 12 | private readonly ReleaseRepository _repository; 13 | 14 | public IRepository RepositoryRef => _repository?.GetRepositoryReference(); 15 | 16 | public string ReleaseBranch => _repository?.ReleaseBranch; 17 | 18 | public string RepositoryPath => _repository?.RepositoryPath; 19 | 20 | object ISourceRepositoryProvider.RepositoryRef => _repository; 21 | 22 | private DirectoryBase Directory { get; } 23 | 24 | public OnDiskGitRepository(string repoPath, string releaseBranch, IFileSystem fileSystem) 25 | { 26 | Directory = fileSystem.Directory; 27 | 28 | var gitPath = FindGitPath(repoPath); 29 | var repoRef = new Repository(gitPath); 30 | 31 | _repository = new ReleaseRepository(gitPath, releaseBranch, repoRef); 32 | } 33 | 34 | private string FindGitPath(string currentPath, int maxDepth = 4, int currentDepth = 0) 35 | { 36 | try 37 | { 38 | currentDepth++; 39 | 40 | if (currentDepth > maxDepth) throw new Exception(); 41 | 42 | var dir = Directory.EnumerateDirectories(currentPath, ".git").FirstOrDefault(); 43 | 44 | if (string.IsNullOrEmpty(dir)) 45 | return FindGitPath(Directory.GetParent(currentPath).FullName, maxDepth, currentDepth); 46 | else 47 | return dir; 48 | } 49 | catch (Exception ex) 50 | { 51 | throw new System.IO.FileNotFoundException("Unable to locate git repository for project.", ex); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/ProjectReleaser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Abstractions; 3 | using LibGit2Sharp; 4 | using SemanticRelease.Extensibility; 5 | 6 | namespace SemanticRelease.CommitAnalyzer 7 | { 8 | public class ProjectReleaser : IProjectReleaseStrategy 9 | { 10 | private readonly ISourceRepositoryProvider _gitRepo; 11 | private readonly IProjectManager _project; 12 | 13 | private DirectoryBase Directory { get; } 14 | private PathBase Path { get; } 15 | 16 | public ProjectReleaser( 17 | IProjectManager project, 18 | ISourceRepositoryProvider repo, 19 | IFileSystem fileSystem) 20 | { 21 | Directory = fileSystem.Directory; 22 | Path = fileSystem.Path; 23 | 24 | _project = project; 25 | _gitRepo = (ISourceRepositoryProvider)repo; 26 | } 27 | 28 | public void PrepareForRelease() 29 | { 30 | var repo = _gitRepo.RepositoryRef; 31 | 32 | int workDirLength = Directory.GetParent(_gitRepo.RepositoryPath).FullName.Length; 33 | string workingPath = Path.GetFullPath(_project.ProjectPath).Substring(workDirLength + 1); 34 | 35 | repo.Index.Add(workingPath); 36 | var signature = new Signature("jenkins", "jenkins", DateTimeOffset.UtcNow); 37 | var vCommit = repo.Commit($"chore(release): Releasing {_project.GetVersion()}", signature, signature); 38 | var vTag = repo.ApplyTag(_project.GetVersion()); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/SemanticRelease.CommitAnalyzer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | SemanticRelease.CommitAnalyzer 5 | SemanticRelease.CommitAnalyzer 6 | 2.2.0 7 | 8 | 9 | 10 | A. Leonard 11 | https://github.com/axlj45/dotnet-semantic-release/blob/trunk/LICENSE 12 | false 13 | Version package per semantic version specification. 14 | semantic-versioning semantic-release 15 | git 16 | https://github.com/axlj45/dotnet-semantic-release 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/SemanticRelease.CommitAnalyzer/VersionCalculator.cs: -------------------------------------------------------------------------------- 1 | using SemanticRelease.Extensibility; 2 | using SemanticRelease.Extensibility.Model; 3 | using SemanticVersion = SemVer.Version; 4 | 5 | namespace SemanticRelease.CommitAnalyzer 6 | { 7 | internal class VersionCalculator : IVersionCalculator 8 | { 9 | 10 | public SemanticReleaseVersion GetNextVersion(Release lastRelease, ReleaseType releaseType) 11 | { 12 | var lastVersion = lastRelease?.Version; 13 | 14 | if (lastVersion == null) return new SemanticReleaseVersion("1.0.0"); 15 | 16 | var testVersion = new SemanticVersion(lastVersion); 17 | 18 | int nextMajor = testVersion.Major; 19 | int nextMinor = testVersion.Minor; 20 | int nextPatch = testVersion.Patch; 21 | 22 | switch (releaseType) 23 | { 24 | case ReleaseType.MAJOR: 25 | nextMajor += 1; 26 | nextMinor = 0; 27 | nextPatch = 0; 28 | break; 29 | 30 | case ReleaseType.MINOR: 31 | nextMinor += 1; 32 | nextPatch = 0; 33 | break; 34 | 35 | case ReleaseType.PATCH: 36 | nextPatch += 1; 37 | break; 38 | 39 | default: 40 | throw new NoOpReleaseException(lastVersion); 41 | } 42 | 43 | var newVersion = new SemanticVersion($"{nextMajor}.{nextMinor}.{nextPatch}"); 44 | 45 | return new SemanticReleaseVersion(newVersion.ToString()); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Core/Program.cs: -------------------------------------------------------------------------------- 1 | using McMaster.Extensions.CommandLineUtils; 2 | using SemanticRelease.Core.CLI; 3 | 4 | namespace SemanticRelease.Core 5 | { 6 | [Command(Description = "Semantic Release")] 7 | public class Program 8 | { 9 | public static int Main(string[] args) => CommandLineApplication.Execute(args); 10 | } 11 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Core/SemanticRelease.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 2.2.0 5 | SemanticRelease.Core 6 | SemanticRelease.Core 7 | 8 | 9 | 10 | A. Leonard 11 | https://github.com/axlj45/dotnet-semantic-release/blob/trunk/LICENSE 12 | false 13 | Core libraries for the Semantic Release Tool and Global Tool 14 | semantic-versioning semantic-release 15 | git 16 | https://github.com/axlj45/dotnet-semantic-release 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/SemanticRelease.Core/cli/CurrentVersionCli.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SemanticRelease.CommitAnalyzer; 3 | using McMaster.Extensions.CommandLineUtils; 4 | using System.IO.Abstractions; 5 | 6 | namespace SemanticRelease.Core.CLI 7 | { 8 | [Command(Description = "Get version of target project")] 9 | public class CurrentVersionCli : ToolCliBase 10 | { 11 | private readonly FileSystem _fileSystem; 12 | 13 | private SemanticReleaseEntry Parent { get; set; } 14 | 15 | public CurrentVersionCli() 16 | { 17 | _fileSystem = new FileSystem(); 18 | } 19 | 20 | protected override int OnExecute(CommandLineApplication app) 21 | { 22 | var targetProject = TargetProject ?? Parent.TargetProject; 23 | 24 | var project = new DotnetProjectParser(targetProject ?? System.Environment.CurrentDirectory, _fileSystem); 25 | 26 | Console.WriteLine(project.GetVersion()); 27 | 28 | return 0; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Core/cli/PublishCli.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO.Abstractions; 3 | using McMaster.Extensions.CommandLineUtils; 4 | using SemanticRelease.CommitAnalyzer; 5 | using SemanticRelease.Core.CLI; 6 | 7 | namespace SemanticRelease.Core.CLI 8 | { 9 | [Command(Description = "Create new version and prepare for release")] 10 | public class PublishCli : ToolCliBase 11 | { 12 | private readonly FileSystem _fileSystem; 13 | 14 | private SemanticReleaseEntry Parent { get; set; } 15 | 16 | 17 | public PublishCli() 18 | { 19 | _fileSystem = new FileSystem(); 20 | } 21 | 22 | protected override int OnExecute(CommandLineApplication app) 23 | { 24 | var workingDir = TargetProject ?? Parent.TargetProject ?? System.Environment.CurrentDirectory; 25 | var releaseBranch = ReleaseBranch ?? Parent.ReleaseBranch ?? "trunk"; 26 | 27 | try 28 | { 29 | var repository = new OnDiskGitRepository(workingDir, releaseBranch, _fileSystem); 30 | 31 | var publisher = new GitHubSourcePublisher(repository); 32 | var commitAnalyzer = new DefaultCommitAnalyzer(repository); 33 | 34 | var lastRelease = commitAnalyzer.GetLastRelease(); 35 | 36 | publisher.Push(lastRelease.Version); 37 | } 38 | catch (Exception ex) 39 | { 40 | Console.WriteLine(ex.Message); 41 | return 1; 42 | } 43 | 44 | return 0; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Core/cli/ReleaseCli.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SemanticRelease.CommitAnalyzer; 3 | using SemanticRelease.Extensibility.Model; 4 | using McMaster.Extensions.CommandLineUtils; 5 | using SemanticRelease.Extensibility; 6 | using System.IO.Abstractions; 7 | 8 | namespace SemanticRelease.Core.CLI 9 | { 10 | [Command(Description = "Create new version and prepare for release")] 11 | public class ReleaseCli : ToolCliBase 12 | { 13 | private readonly FileSystem _fileSystem; 14 | 15 | private SemanticReleaseEntry Parent { get; set; } 16 | 17 | [Option("-f|--fail-on-no-release", Description = "Return non-zero exit code when no release is required.")] 18 | public bool ThrowOnNoOp { get; set; } 19 | 20 | [Option("-d|--detached-head", Description = "Work with a detached HEAD (for example, when building on Azure pipelines)")] 21 | public bool DetachedHead { get; set; } 22 | 23 | public ReleaseCli() 24 | { 25 | _fileSystem = new FileSystem(); 26 | } 27 | 28 | protected override int OnExecute(CommandLineApplication app) 29 | { 30 | var workingDir = TargetProject ?? Parent.TargetProject ?? System.Environment.CurrentDirectory; 31 | var releaseBranch = ReleaseBranch ?? Parent.ReleaseBranch ?? "trunk"; 32 | 33 | try 34 | { 35 | var repository = new OnDiskGitRepository(workingDir, releaseBranch, _fileSystem); 36 | 37 | var preconditions = new DefaultPreConditions(repository); 38 | preconditions.Verify(DetachedHead); 39 | 40 | var commitAnalyzer = new DefaultCommitAnalyzer(repository); 41 | commitAnalyzer.CommitEvent += OnCommitEvent; 42 | var nextRelease = commitAnalyzer.CalculateNextRelease(); 43 | 44 | var project = new DotnetProjectParser(workingDir, _fileSystem); 45 | project.SetVersion(nextRelease.Version); 46 | 47 | var releaser = new ProjectReleaser(project, repository, _fileSystem); 48 | releaser.PrepareForRelease(); 49 | } 50 | catch (NoOpReleaseException ex) 51 | { 52 | Console.WriteLine($"There have been no releasable commits since v{ex.LastVersion}"); 53 | return ThrowOnNoOp ? 1 : 0; 54 | } 55 | catch (Exception ex) 56 | { 57 | Console.WriteLine(ex.Message); 58 | return 1; 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | private void OnCommitEvent(object sender, CommitStatusEventArgs e) 65 | { 66 | Console.WriteLine(e.Message); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Core/cli/SemanticReleaseEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using SemanticRelease.CommitAnalyzer; 4 | using McMaster.Extensions.CommandLineUtils; 5 | 6 | namespace SemanticRelease.Core.CLI 7 | { 8 | [Command("semantic-release", Description = "Semantic Release")] 9 | [VersionOptionFromMember("--version", MemberName = nameof(GetVersion))] 10 | [Subcommand("release", typeof(ReleaseCli))] 11 | [Subcommand("project-version", typeof(CurrentVersionCli))] 12 | [Subcommand("publish", typeof(PublishCli))] 13 | public class SemanticReleaseEntry : ToolCliBase 14 | { 15 | private static string GetVersion() 16 | => Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; 17 | 18 | protected override int OnExecute(CommandLineApplication app) 19 | { 20 | base.OnExecute(app); 21 | return 0; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Core/cli/ToolCliBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using McMaster.Extensions.CommandLineUtils; 5 | 6 | namespace SemanticRelease.Core.CLI 7 | { 8 | [HelpOption("--help")] 9 | public class ToolCliBase 10 | { 11 | [Option("-p|--project-path ", Description = "The directory that contains the csproj project to release.")] 12 | [FileOrDirectoryExists] 13 | public string TargetProject { get; set; } 14 | 15 | [Option("-b|--release-branch ", Description = "Branch that is permitted to perform a release. Default is 'trunk'")] 16 | public string ReleaseBranch { get; set; } 17 | 18 | protected virtual int OnExecute(CommandLineApplication app) 19 | { 20 | app.ShowHelp(); 21 | return 0; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/CommitStatusEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SemanticRelease.Extensibility 4 | { 5 | public class CommitStatusEventArgs : EventArgs 6 | { 7 | public string Message { get; } 8 | 9 | public CommitStatusEventArgs(string message) 10 | { 11 | this.Message = message; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/ICommitAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SemanticRelease.Extensibility.Model; 3 | 4 | namespace SemanticRelease.Extensibility 5 | { 6 | public interface ICommitAnalyzer 7 | { 8 | Release CalculateNextRelease(); 9 | 10 | event EventHandler CommitEvent; 11 | } 12 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/IPostReleaseAction.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility 2 | { 3 | public interface IPostReleaseAction 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/IPreConditionVerifier.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility 2 | { 3 | public interface IPreConditionVerifier 4 | { 5 | void Verify(bool detachedHead); 6 | } 7 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/IProjectManager.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility 2 | { 3 | public interface IProjectManager 4 | { 5 | string ProjectPath { get; } 6 | string GetVersion(); 7 | void SetVersion(string version); 8 | } 9 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/IProjectReleaseStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility 2 | { 3 | public interface IProjectReleaseStrategy 4 | { 5 | void PrepareForRelease(); 6 | } 7 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/IReleaseVerifier.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility 2 | { 3 | public interface IReleaseVerifier 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/ISourcePublisher.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility 2 | { 3 | public interface ISourcePublisher 4 | { 5 | void Push(); 6 | void Push(string tagToPush); 7 | } 8 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/ISourceRepositoryProvider.cs: -------------------------------------------------------------------------------- 1 | using SemanticRelease.Extensibility.Model; 2 | 3 | namespace SemanticRelease.Extensibility 4 | { 5 | public interface ISourceRepositoryProvider 6 | { 7 | string ReleaseBranch { get; } 8 | string RepositoryPath { get; } 9 | 10 | object RepositoryRef { get; } 11 | } 12 | 13 | public interface ISourceRepositoryProvider : ISourceRepositoryProvider 14 | { 15 | new T RepositoryRef { get; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/IVersionCalculator.cs: -------------------------------------------------------------------------------- 1 | using SemanticRelease.Extensibility.Model; 2 | 3 | namespace SemanticRelease.Extensibility 4 | { 5 | public interface IVersionCalculator 6 | { 7 | SemanticReleaseVersion GetNextVersion(Release lastRelease, ReleaseType releaseType); 8 | } 9 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/Model/NoOpReleaseException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace SemanticRelease.Extensibility.Model 5 | { 6 | [Serializable] 7 | public class NoOpReleaseException : Exception 8 | { 9 | public string LastVersion { get; } 10 | 11 | public NoOpReleaseException(string lastVersion) : base() 12 | { 13 | LastVersion = lastVersion; 14 | } 15 | public NoOpReleaseException(string lastVersion, string message) : base(message) 16 | { 17 | LastVersion = lastVersion; 18 | } 19 | 20 | public NoOpReleaseException(string lastVersion, string message, Exception inner) 21 | : base(message, inner) 22 | { 23 | LastVersion = lastVersion; 24 | } 25 | protected NoOpReleaseException( 26 | SerializationInfo info, 27 | StreamingContext context) : base(info, context) { } 28 | } 29 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/Model/Release.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SemanticRelease.Extensibility.Model 4 | { 5 | public class Release 6 | { 7 | public Release(string version, string sha) : this(version, sha, null) 8 | { } 9 | 10 | public Release(string version, string sha, IEnumerable commitsSinceLastRelease) 11 | { 12 | this.Version = version; 13 | this.Sha = sha; 14 | this.CommitsSinceLastRelease = commitsSinceLastRelease; 15 | } 16 | 17 | public string Version { get; private set; } 18 | public string Sha { get; private set; } 19 | public IEnumerable CommitsSinceLastRelease { get; private set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/Model/ReleaseCommit.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility.Model 2 | { 3 | public class ReleaseCommit 4 | { 5 | public ReleaseCommit(int index, string sha, string message) 6 | { 7 | this.Sha = sha; 8 | this.Index = index; 9 | this.Message = message; 10 | } 11 | 12 | public int Index { get; } 13 | public string Sha { get; } 14 | public string Message { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/Model/ReleaseRepository.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility.Model 2 | { 3 | public abstract class AbstractReleaseRepository 4 | { 5 | public virtual string ReleaseBranch { get; } 6 | public virtual string RepositoryPath { get; } 7 | protected virtual object RepositoryRef { get; } 8 | 9 | protected AbstractReleaseRepository(string repositoryPath, string releaseBranchName, object repositoryReference) 10 | { 11 | this.RepositoryPath = repositoryPath; 12 | this.ReleaseBranch = releaseBranchName; 13 | this.RepositoryRef = repositoryReference; 14 | } 15 | } 16 | public class ReleaseRepository : AbstractReleaseRepository 17 | { 18 | 19 | public ReleaseRepository(string repositoryPath, string releaseBranchName, T repositoryReference) 20 | : base(repositoryPath, releaseBranchName, repositoryReference) { } 21 | 22 | public T GetRepositoryReference() 23 | { 24 | return (T)RepositoryRef; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/Model/SemanticReleaseVersion.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility.Model 2 | { 3 | public class SemanticReleaseVersion 4 | { 5 | public string Version { get; } 6 | 7 | public SemanticReleaseVersion(string version) 8 | { 9 | this.Version = version; 10 | } 11 | 12 | public override string ToString() => this.Version; 13 | } 14 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/ReleaseType.cs: -------------------------------------------------------------------------------- 1 | namespace SemanticRelease.Extensibility 2 | { 3 | public enum ReleaseType 4 | { 5 | MAJOR = 6, 6 | PREMAJOR = 3, 7 | MINOR = 5, 8 | PREMINOR = 2, 9 | PATCH = 4, 10 | PREPATCH = 1, 11 | NONE = 0 12 | } 13 | } -------------------------------------------------------------------------------- /src/SemanticRelease.Extensibility/SemanticRelease.Extensibility.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 2.2.0 5 | SemanticRelease.Extensibility 6 | SemanticRelease.Extensibility 7 | 8 | 9 | 10 | A. Leonard 11 | https://github.com/axlj45/dotnet-semantic-release/blob/trunk/LICENSE 12 | false 13 | Library for extending dotnet-semanticrelease. 14 | semantic-versioning semantic-release 15 | git 16 | https://github.com/axlj45/dotnet-semantic-release 17 | 18 | -------------------------------------------------------------------------------- /src/SemanticRelease.GlobalTool/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SemanticRelease.GlobalTool 4 | { 5 | class Program 6 | { 7 | static int Main(string[] args) => SemanticRelease.Core.Program.Main(args); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SemanticRelease.GlobalTool/SemanticRelease.GlobalTool.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | Exe 5 | semantic-release 6 | SemanticRelease.GlobalTool 7 | SemanticRelease.GlobalTool 8 | 2.3.0 9 | 2.3.0 10 | True 11 | True 12 | 13 | 14 | 15 | 16 | A. Leonard 17 | https://github.com/axlj45/dotnet-semantic-release/blob/trunk/LICENSE 18 | false 19 | Version package per semantic version specification. 20 | semantic-versioning semantic-release 21 | git 22 | https://github.com/axlj45/dotnet-semantic-release 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/SemanticRelease.Tool/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SemanticRelease.Tool 4 | { 5 | class Program 6 | { 7 | static int Main(string[] args) => SemanticRelease.Core.Program.Main(args); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SemanticRelease.Tool/SemanticRelease.Tool.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | Exe 5 | SemanticRelease.Tool 6 | dotnet-semanticrelease 7 | 2.3.0 8 | True 9 | 10 | 11 | A. Leonard 12 | https://github.com/axlj45/dotnet-semantic-release/blob/trunk/LICENSE 13 | false 14 | Version package per semantic version specification. 15 | semantic-versioning semantic-release 16 | git 17 | https://github.com/axlj45/dotnet-semantic-release 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/dotnet-semantic-release.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticRelease.CommitAnalyzer", "SemanticRelease.CommitAnalyzer\SemanticRelease.CommitAnalyzer.csproj", "{55AE256E-F66A-4309-9A15-6B9405F1246A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticRelease.Extensibility", "SemanticRelease.Extensibility\SemanticRelease.Extensibility.csproj", "{CBBC497D-FB07-4882-B7DC-3B7EAC014573}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticRelease.Core", "SemanticRelease.Core\SemanticRelease.Core.csproj", "{0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticRelease.GlobalTool", "SemanticRelease.GlobalTool\SemanticRelease.GlobalTool.csproj", "{A06BE319-8084-43A0-9C97-F7532E2482C7}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticRelease.Tool", "SemanticRelease.Tool\SemanticRelease.Tool.csproj", "{91D21FC5-8F14-4F30-AA16-6F46D2B449AD}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticRelease.CommitAnalyzer.Tests", "SemanticRelease.CommitAnalyzer.Tests\SemanticRelease.CommitAnalyzer.Tests.csproj", "{4EC641CE-6626-491F-A481-6FF7D29858AD}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Debug|x64 = Debug|x64 22 | Debug|x86 = Debug|x86 23 | Release|Any CPU = Release|Any CPU 24 | Release|x64 = Release|x64 25 | Release|x86 = Release|x86 26 | EndGlobalSection 27 | GlobalSection(SolutionProperties) = preSolution 28 | HideSolutionNode = FALSE 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Debug|x64.ActiveCfg = Debug|Any CPU 34 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Debug|x64.Build.0 = Debug|Any CPU 35 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Debug|x86.ActiveCfg = Debug|Any CPU 36 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Debug|x86.Build.0 = Debug|Any CPU 37 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Release|x64.ActiveCfg = Release|Any CPU 40 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Release|x64.Build.0 = Release|Any CPU 41 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Release|x86.ActiveCfg = Release|Any CPU 42 | {11FFB322-FEB1-4C28-B991-A3DD75C8AE0E}.Release|x86.Build.0 = Release|Any CPU 43 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Debug|x64.ActiveCfg = Debug|Any CPU 46 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Debug|x64.Build.0 = Debug|Any CPU 47 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Debug|x86.ActiveCfg = Debug|Any CPU 48 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Debug|x86.Build.0 = Debug|Any CPU 49 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Release|x64.ActiveCfg = Release|Any CPU 52 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Release|x64.Build.0 = Release|Any CPU 53 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Release|x86.ActiveCfg = Release|Any CPU 54 | {55AE256E-F66A-4309-9A15-6B9405F1246A}.Release|x86.Build.0 = Release|Any CPU 55 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Debug|x64.ActiveCfg = Debug|Any CPU 58 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Debug|x64.Build.0 = Debug|Any CPU 59 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Debug|x86.ActiveCfg = Debug|Any CPU 60 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Debug|x86.Build.0 = Debug|Any CPU 61 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Release|x64.ActiveCfg = Release|Any CPU 64 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Release|x64.Build.0 = Release|Any CPU 65 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Release|x86.ActiveCfg = Release|Any CPU 66 | {CBBC497D-FB07-4882-B7DC-3B7EAC014573}.Release|x86.Build.0 = Release|Any CPU 67 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Debug|x64.ActiveCfg = Debug|Any CPU 70 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Debug|x64.Build.0 = Debug|Any CPU 71 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Debug|x86.ActiveCfg = Debug|Any CPU 72 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Debug|x86.Build.0 = Debug|Any CPU 73 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Release|x64.ActiveCfg = Release|Any CPU 76 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Release|x64.Build.0 = Release|Any CPU 77 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Release|x86.ActiveCfg = Release|Any CPU 78 | {0C2E13AE-2B5C-42A9-94FD-F6CEC07A41B0}.Release|x86.Build.0 = Release|Any CPU 79 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Debug|x64.ActiveCfg = Debug|Any CPU 82 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Debug|x64.Build.0 = Debug|Any CPU 83 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Debug|x86.ActiveCfg = Debug|Any CPU 84 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Debug|x86.Build.0 = Debug|Any CPU 85 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Release|Any CPU.ActiveCfg = Release|Any CPU 86 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Release|Any CPU.Build.0 = Release|Any CPU 87 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Release|x64.ActiveCfg = Release|Any CPU 88 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Release|x64.Build.0 = Release|Any CPU 89 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Release|x86.ActiveCfg = Release|Any CPU 90 | {A06BE319-8084-43A0-9C97-F7532E2482C7}.Release|x86.Build.0 = Release|Any CPU 91 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 92 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Debug|Any CPU.Build.0 = Debug|Any CPU 93 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Debug|x64.ActiveCfg = Debug|Any CPU 94 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Debug|x64.Build.0 = Debug|Any CPU 95 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Debug|x86.ActiveCfg = Debug|Any CPU 96 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Debug|x86.Build.0 = Debug|Any CPU 97 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Release|Any CPU.ActiveCfg = Release|Any CPU 98 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Release|Any CPU.Build.0 = Release|Any CPU 99 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Release|x64.ActiveCfg = Release|Any CPU 100 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Release|x64.Build.0 = Release|Any CPU 101 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Release|x86.ActiveCfg = Release|Any CPU 102 | {91D21FC5-8F14-4F30-AA16-6F46D2B449AD}.Release|x86.Build.0 = Release|Any CPU 103 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 104 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Debug|Any CPU.Build.0 = Debug|Any CPU 105 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Debug|x64.ActiveCfg = Debug|Any CPU 106 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Debug|x64.Build.0 = Debug|Any CPU 107 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Debug|x86.ActiveCfg = Debug|Any CPU 108 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Debug|x86.Build.0 = Debug|Any CPU 109 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Release|Any CPU.ActiveCfg = Release|Any CPU 110 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Release|Any CPU.Build.0 = Release|Any CPU 111 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Release|x64.ActiveCfg = Release|Any CPU 112 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Release|x64.Build.0 = Release|Any CPU 113 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Release|x86.ActiveCfg = Release|Any CPU 114 | {4EC641CE-6626-491F-A481-6FF7D29858AD}.Release|x86.Build.0 = Release|Any CPU 115 | EndGlobalSection 116 | EndGlobal 117 | --------------------------------------------------------------------------------