Skip to content

Commit

Permalink
feat: support pre-releases (#50)
Browse files Browse the repository at this point in the history
* feat: implement tests for pre-release functionality

* fix: implement stable to pre-release updates

* fix: implement pre-release to pre-release

* fix: release to prerelease

* fix: handle insignificant commits in WorkingCopy

* feat: prevent lower versions to be generated and add tests

* feat: implement prerelease cmd option

* chore: generate a new help section and doc pre-releases

* chore: fix code style

* chore: fix minor code style issues

* chore: stable prerelease parsing and code style improvement
  • Loading branch information
saintedlama committed Feb 11, 2022
1 parent 80608a8 commit 2aa68c7
Show file tree
Hide file tree
Showing 11 changed files with 560 additions and 75 deletions.
45 changes: 39 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,26 @@ dotnet tool install --global Versionize
## Usage

```bash
Usage: versionize [options]
Usage: versionize [command] [options]

Options:
-?|-h|--help Show help information
-v|--version Show version information
-?|-h|--help Show help information.
-v|--version Show version information.
-w|--workingDir <WORKING_DIRECTORY> Directory containing projects to version
-d|--dry-run Skip changing versions in projects, changelog generation and git commit
--skip-dirty Skip git dirty check
-r|--release-as <VERSION> Specify the release version manually
--silent Suppress output to console
--skip-commit Skip commit and git tag after updating changelog and incrementing the version
-i|--ignore-insignificant-commits Do not bump the version if no significant commits (fix, feat or BREAKING) are found
--skip-commit Skip commit and git tag after updating changelog and incrementing the
version
-i|--ignore-insignificant-commits Do not bump the version if no significant commits (fix, feat or BREAKING)
are found
--changelog-all Include all commits in the changelog not just fix, feat and breaking changes
--commit-suffix Suffix to be added to the end of the release commit message (e.g. [skip ci])
-p|--pre-release Release as pre-release version with given pre release label.

Commands:
inspect Prints the current version to stdout
```
## Supported commit types
Expand Down Expand Up @@ -133,6 +139,33 @@ versionize
Will update CHANGELOG.md, add git tags and commit everything. Note that the version in `SomeProject.csproj` is now `2.0.0` since
versionize detected a breaking change since the commit note `BREAKING CHANGE` was used above.
### Pre-releases
Versionize supports creating pre-release versions by using the `--pre-release` flag with a pre-release label, for example `alpha`.
The following workflow illustrates how pre-release workflows with versionize work.
```shell
> git commit -a -m "chore: initial commit"
> versionize
// Generates version v1.0.0
> git commit -a -m "feat: some feature"
> versionize --pre-release alpha
// Generates version v1.1.0-alpha.0
> git commit -a -m "feat: some additional feature"
> versionize --pre-release alpha
// Generates version v1.1.0-alpha.1
> git commit -a -m "feat: some breaking feature" -m "BREAKING CHANGE: This is a breaking change"
> versionize --pre-release alpha
// Generates version v2.0.0-alpha.0
> versionize
// Generates version v2.0.0
```
## Configuration
You can configure `versionize` either by creating a `.versionize` JSON file the working directory.
Expand Down Expand Up @@ -179,7 +212,7 @@ dotnet test --logger prettier
## Roadmap
* [ ] Pre Releases to allow creating beta.1, beta.2 versions
* [x] Pre Releases to allow creating beta.1, beta.2 versions
* [x] Support .versionrc like "standard-version" does
* [ ] Support mono repo joint and disjoint version strategies
* [x] ~~--silent command line switch to suppress commandline output~~
Expand Down
275 changes: 241 additions & 34 deletions Versionize.Tests/VersionIncrementStrategyTests.cs
Original file line number Diff line number Diff line change
@@ -1,73 +1,280 @@
using Shouldly;
using NuGet.Versioning;
using Shouldly;
using Xunit;
using Version = NuGet.Versioning.SemanticVersion;

namespace Versionize.Tests;

public class VersionIncrementStrategyTests
{
[Fact]
public void ShouldIncrementPatchVersionForEmptyCommits()
{
var strategy = VersionIncrementStrategy.CreateFrom(new List<ConventionalCommit>());
strategy.NextVersion(new Version(1, 1, 1)).ShouldBe(new Version(1, 1, 2));
}

[Fact]
public void ShouldNotIncrementPatchVersionForEmptyCommitsIfIgnoreInsignificantIsGiven()
{
var strategy = VersionIncrementStrategy.CreateFrom(new List<ConventionalCommit>());
strategy.NextVersion(new Version(1, 1, 1), true).ShouldBe(new Version(1, 1, 1));
var strategy = new VersionIncrementStrategy(new List<ConventionalCommit>());
strategy.NextVersion(new SemanticVersion(1, 1, 1)).ShouldBe(new SemanticVersion(1, 1, 1));
}

[Fact]
public void ShouldNotIncrementPatchVersionForInsignificantCommitsIfIgnoreInsignificantIsGiven()
{
var strategy = VersionIncrementStrategy.CreateFrom(new List<ConventionalCommit>
{
new ConventionalCommit { Type = "chore" }
});
var strategy = new VersionIncrementStrategy(new List<ConventionalCommit>
{
new ConventionalCommit { Type = "chore" }
});

strategy.NextVersion(new Version(1, 1, 1), true).ShouldBe(new Version(1, 1, 1));
strategy.NextVersion(new SemanticVersion(1, 1, 1)).ShouldBe(new SemanticVersion(1, 1, 1));
}

[Fact]
public void ShouldIncrementPatchVersionForFixCommitsIfIgnoreInsignificantIsGiven()
{
var strategy = VersionIncrementStrategy.CreateFrom(new List<ConventionalCommit>
{
new ConventionalCommit { Type = "fix" }
});
var strategy = new VersionIncrementStrategy(new List<ConventionalCommit>
{
new ConventionalCommit { Type = "fix" }
});

strategy.NextVersion(new Version(1, 1, 1), true).ShouldBe(new Version(1, 1, 2));
strategy.NextVersion(new SemanticVersion(1, 1, 1)).ShouldBe(new SemanticVersion(1, 1, 2));
}

[Fact]
public void ShouldIncrementMinorVersionForFeatures()
{
var strategy = VersionIncrementStrategy.CreateFrom(new List<ConventionalCommit>
{
new ConventionalCommit { Type = "feat" }
});
var strategy = new VersionIncrementStrategy(new List<ConventionalCommit>
{
new ConventionalCommit { Type = "feat" }
});

strategy.NextVersion(new Version(1, 1, 1)).ShouldBe(new Version(1, 2, 0));
strategy.NextVersion(new SemanticVersion(1, 1, 1)).ShouldBe(new SemanticVersion(1, 2, 0));
}

[Fact]
public void ShouldIncrementMajorVersionForBreakingChanges()
{
var strategy = VersionIncrementStrategy.CreateFrom(new List<ConventionalCommit>
var strategy = new VersionIncrementStrategy(new List<ConventionalCommit>
{
new ConventionalCommit
{
new ConventionalCommit
Type = "chore",
Notes = new List<ConventionalCommitNote>
{
Type = "chore",
Notes = new List<ConventionalCommitNote>
{
new ConventionalCommitNote { Title = "BREAKING CHANGE"}
}
new ConventionalCommitNote { Title = "BREAKING CHANGE"}
}
}
});

strategy.NextVersion(new SemanticVersion(1, 1, 1)).ShouldBe(new SemanticVersion(2, 0, 0));
}

[Theory]
[MemberData(nameof(StableToPrerelease))]
public void ShouldIncrementVersionFromStableToPrerelease(TestScenario testScenario)
{
var strategy = new VersionIncrementStrategy(testScenario.Commits);

var nextVersion = strategy.NextVersion(testScenario.FromVersion, testScenario.PrereleaseLabel);

nextVersion.ShouldBe(testScenario.ExpectedVersion);
}


[Theory]
[MemberData(nameof(PrereleaseToPrerelease))]
public void ShouldIncrementVersionFromPrereleaseToPrerelease(TestScenario testScenario)
{
var strategy = new VersionIncrementStrategy(testScenario.Commits);

var nextVersion = strategy.NextVersion(testScenario.FromVersion, testScenario.PrereleaseLabel);

nextVersion.ShouldBe(testScenario.ExpectedVersion);
}

[Theory]
[MemberData(nameof(PrereleaseToStable))]
public void ShouldIncrementVersionFromPrereleaseToStable(TestScenario testScenario)
{
var strategy = new VersionIncrementStrategy(testScenario.Commits);

var nextVersion = strategy.NextVersion(testScenario.FromVersion, testScenario.PrereleaseLabel);

nextVersion.ShouldBe(testScenario.ExpectedVersion);
}

public static IEnumerable<object[]> StableToPrerelease()
{
// From stable release to pre-release
yield return Scenario("release number increment from major with breaking change commit to major alpha")
.FromVersion("1.0.0")
.GivenCommit("feat", "BREAKING CHANGE")
.Prerelease("alpha")
.ExpectVersion("2.0.0-alpha.0");

yield return Scenario("release number increment from major with feat commit to minor alpha")
.FromVersion("1.0.0")
.GivenCommit("feat")
.Prerelease("alpha")
.ExpectVersion("1.1.0-alpha.0");

yield return Scenario("release number increment from major with fix commit to patch alpha")
.FromVersion("1.0.0")
.GivenCommit("fix")
.Prerelease("alpha")
.ExpectVersion("1.0.1-alpha.0");
}

public static IEnumerable<object[]> PrereleaseToPrerelease()
{
yield return Scenario("pre-release number increment from major with breaking change commit")
.FromVersion("1.0.0-alpha.0")
.GivenCommit("feat", "BREAKING CHANGE")
.Prerelease("alpha")
.ExpectVersion("1.0.0-alpha.1");

yield return Scenario("version increment from minor to major with breaking change commit")
.FromVersion("1.1.0-alpha.0")
.GivenCommit("feat", "BREAKING CHANGE")
.Prerelease("alpha")
.ExpectVersion("2.0.0-alpha.0");

yield return Scenario("pre-release number increment from minor with fix commit")
.FromVersion("1.1.0-alpha.0")
.GivenCommit("fix")
.Prerelease("alpha")
.ExpectVersion("1.1.0-alpha.1");

yield return Scenario("pre-release number increment from minor with feat commit")
.FromVersion("1.1.0-alpha.0")
.GivenCommit("feat")
.Prerelease("alpha")
.ExpectVersion("1.1.0-alpha.1");

yield return Scenario("ignore insignificant commit")
.FromVersion("1.0.0-alpha.0")
.GivenCommit("chore")
.Prerelease("alpha")
.ExpectVersion("1.0.0-alpha.0");

yield return Scenario("pre-release number increment from minor with fix commit with new pre-release label")
.FromVersion("1.1.0-alpha.0")
.GivenCommit("fix")
.Prerelease("beta")
.ExpectVersion("1.1.0-beta.0");

yield return Scenario("exit for lower version as existed commit")
.FromVersion("1.0.0-alpha.0")
.GivenCommit("chore")
.Prerelease("alpha")
.ExpectVersion("1.0.0-alpha.0");
}

public static IEnumerable<object[]> PrereleaseToStable()
{
// Release pre-releases from pre-release versions in next pre-release label
yield return Scenario("release from major pre-release with feat commit")
.FromVersion("2.0.0-alpha.2")
.GivenCommit("feat")
.ExpectVersion("2.0.0");

yield return Scenario("release from major pre-release with fix commit")
.FromVersion("1.0.0-alpha.0")
.GivenCommit("fix")
.ExpectVersion("1.0.0");

yield return Scenario("release from minor pre-release with breaking change commit")
.FromVersion("1.1.0-alpha.0")
.GivenCommit("fix", "BREAKING CHANGE")
.ExpectVersion("2.0.0");

yield return Scenario("release from patch pre-release with feat commit")
.FromVersion("1.0.1-alpha.0")
.GivenCommit("feat")
.ExpectVersion("1.1.0");
}

public static TestScenarioBuilder Scenario(string description)
{
var builder = new TestScenarioBuilder();
return builder.DescribedBy(description);
}

public class TestScenarioBuilder
{
private readonly List<ConventionalCommit> _commits = new();
private SemanticVersion _expectedVersion;
private string _prereleaseLabel;
private SemanticVersion _fromVersion;
private string _description;

public TestScenarioBuilder FromVersion(string version)
{
_fromVersion = SemanticVersion.Parse(version);
return this;
}

public TestScenarioBuilder GivenCommits(params ConventionalCommit[] commits)
{
_commits.AddRange(commits);
return this;
}

public TestScenarioBuilder GivenCommit(string type, string note = null)
{
_commits.Add(new ConventionalCommit
{
Type = type,
Notes = string.IsNullOrWhiteSpace(note) ? new List<ConventionalCommitNote>() : new List<ConventionalCommitNote>
{
new ConventionalCommitNote { Title = note }
}
});

strategy.NextVersion(new Version(1, 1, 1)).ShouldBe(new Version(2, 0, 0));
return this;
}

public TestScenarioBuilder Prerelease(string prereleaseLabel = null)
{
_prereleaseLabel = prereleaseLabel;
return this;
}

public TestScenarioBuilder DescribedBy(string description)
{
_description = description;
return this;
}

public object[] ExpectVersion(string expectedVersion)
{
_expectedVersion = SemanticVersion.Parse(expectedVersion);

return Build();
}

public object[] Build()
{
return new object[] {
new TestScenario
{
Commits = _commits,
ExpectedVersion = _expectedVersion,
FromVersion = _fromVersion,
PrereleaseLabel = _prereleaseLabel,
Description = _description,
}
};
}
}

public class TestScenario
{
public List<ConventionalCommit> Commits { get; set; }
public SemanticVersion ExpectedVersion { get; set; }
public string PrereleaseLabel { get; set; }
public SemanticVersion FromVersion { get; set; }

public string Description { get; set; }

public override string ToString()
{
return Description;
}
}
}
Loading

0 comments on commit 2aa68c7

Please sign in to comment.