diff --git a/Directory.Build.props b/Directory.Build.props
index b4b2ee4..6a8a532 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,19 +4,16 @@
Exe
- netcoreapp3.1
+ net6.0
enable
+ enable
- $(NoWarn),SA0001,CA1014
+ $(NoWarn),SA0001,SA1200,SA1516,CA1014,CA1812
6.1.0
4.1.0
0.31.0
-
-
-
-
diff --git a/FakeItEasy.Deploy/Program.cs b/FakeItEasy.Deploy/Program.cs
index ff90510..0438e16 100644
--- a/FakeItEasy.Deploy/Program.cs
+++ b/FakeItEasy.Deploy/Program.cs
@@ -1,147 +1,134 @@
-namespace FakeItEasy.Deploy
+using FakeItEasy.Deploy;
+using FakeItEasy.Tools;
+using Octokit;
+using static FakeItEasy.Tools.ReleaseHelpers;
+using static SimpleExec.Command;
+
+if (args.Length != 1)
+{
+ Console.WriteLine("Illegal arguments. Usage:");
+ Console.WriteLine(" ");
+}
+
+string artifactsFolder = args[0];
+
+var releaseName = GetAppVeyorTagName();
+if (string.IsNullOrEmpty(releaseName))
+{
+ Console.WriteLine("No Appveyor tag name supplied. Not deploying.");
+ return;
+}
+
+var nugetServerUrl = GetNuGetServerUrl();
+var nugetApiKey = GetNuGetApiKey();
+var (repoOwner, repoName) = GetRepositoryName();
+var gitHubClient = GetAuthenticatedGitHubClient();
+
+Console.WriteLine($"Deploying {releaseName}");
+Console.WriteLine($"Looking for GitHub release {releaseName}");
+
+var releases = await gitHubClient.Repository.Release.GetAll(repoOwner, repoName);
+var release = releases.FirstOrDefault(r => r.Name == releaseName)
+ ?? throw new Exception($"Can't find release {releaseName}");
+
+const string artifactsPattern = "*.nupkg";
+
+var artifacts = Directory.GetFiles(artifactsFolder, artifactsPattern);
+if (!artifacts.Any())
+{
+ throw new Exception("Can't find any artifacts to publish");
+}
+
+Console.WriteLine($"Uploading artifacts to GitHub release {releaseName}");
+foreach (var file in artifacts)
+{
+ await UploadArtifactToGitHubReleaseAsync(gitHubClient, release, file);
+}
+
+Console.WriteLine($"Pushing nupkgs to {nugetServerUrl}");
+foreach (var file in artifacts)
+{
+ await UploadPackageToNuGetAsync(file, nugetServerUrl, nugetApiKey);
+}
+
+var issueNumbersInCurrentRelease = GetIssueNumbersReferencedFromReleases(new[] { release });
+var preReleases = GetPreReleasesContributingToThisRelease(release, releases);
+var issueNumbersInPreReleases = GetIssueNumbersReferencedFromReleases(preReleases);
+var newIssueNumbers = issueNumbersInCurrentRelease.Except(issueNumbersInPreReleases);
+
+Console.WriteLine($"Adding 'released as part of' notes to {newIssueNumbers.Count()} issues");
+var commentText = $"This change has been released as part of [{repoName} {releaseName}](https://github.com/{repoOwner}/{repoName}/releases/tag/{releaseName}).";
+await Task.WhenAll(newIssueNumbers.Select(n => gitHubClient.Issue.Comment.Create(repoOwner, repoName, n, commentText)));
+
+Console.WriteLine("Finished deploying");
+
+static IEnumerable GetPreReleasesContributingToThisRelease(Release release, IReadOnlyList releases)
{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading.Tasks;
- using FakeItEasy.Tools;
- using Octokit;
- using static FakeItEasy.Tools.ReleaseHelpers;
- using static SimpleExec.Command;
-
- internal static class Program
+ if (release.Prerelease)
{
- public static async Task Main(string[] args)
- {
- if (args.Length != 1)
- {
- Console.WriteLine("Illegal arguments. Usage:");
- Console.WriteLine(" ");
- }
-
- string artifactsFolder = args[0];
-
- var releaseName = GetAppVeyorTagName();
- if (string.IsNullOrEmpty(releaseName))
- {
- Console.WriteLine("No Appveyor tag name supplied. Not deploying.");
- return;
- }
-
- var nugetServerUrl = GetNuGetServerUrl();
- var nugetApiKey = GetNuGetApiKey();
- var (repoOwner, repoName) = GetRepositoryName();
- var gitHubClient = GetAuthenticatedGitHubClient();
-
- Console.WriteLine($"Deploying {releaseName}");
- Console.WriteLine($"Looking for GitHub release {releaseName}");
-
- var releases = await gitHubClient.Repository.Release.GetAll(repoOwner, repoName);
- var release = releases.FirstOrDefault(r => r.Name == releaseName)
- ?? throw new Exception($"Can't find release {releaseName}");
-
- const string artifactsPattern = "*.nupkg";
-
- var artifacts = Directory.GetFiles(artifactsFolder, artifactsPattern);
- if (!artifacts.Any())
- {
- throw new Exception("Can't find any artifacts to publish");
- }
-
- Console.WriteLine($"Uploading artifacts to GitHub release {releaseName}");
- foreach (var file in artifacts)
- {
- await UploadArtifactToGitHubReleaseAsync(gitHubClient, release, file);
- }
-
- Console.WriteLine($"Pushing nupkgs to {nugetServerUrl}");
- foreach (var file in artifacts)
- {
- await UploadPackageToNuGetAsync(file, nugetServerUrl, nugetApiKey);
- }
-
- var issueNumbersInCurrentRelease = GetIssueNumbersReferencedFromReleases(new[] { release });
- var preReleases = GetPreReleasesContributingToThisRelease(release, releases);
- var issueNumbersInPreReleases = GetIssueNumbersReferencedFromReleases(preReleases);
- var newIssueNumbers = issueNumbersInCurrentRelease.Except(issueNumbersInPreReleases);
-
- Console.WriteLine($"Adding 'released as part of' notes to {newIssueNumbers.Count()} issues");
- var commentText = $"This change has been released as part of [{repoName} {releaseName}](https://github.com/{repoOwner}/{repoName}/releases/tag/{releaseName}).";
- await Task.WhenAll(newIssueNumbers.Select(n => gitHubClient.Issue.Comment.Create(repoOwner, repoName, n, commentText)));
-
- Console.WriteLine("Finished deploying");
- }
-
- private static IEnumerable GetPreReleasesContributingToThisRelease(Release release, IReadOnlyList releases)
- {
- if (release.Prerelease)
- {
- return Enumerable.Empty();
- }
+ return Enumerable.Empty();
+ }
- string baseName = BaseName(release);
- return releases.Where(r => r.Prerelease && BaseName(r) == baseName);
+ string baseName = BaseName(release);
+ return releases.Where(r => r.Prerelease && BaseName(r) == baseName);
- string BaseName(Release release) => release.Name.Split('-')[0];
- }
+ string BaseName(Release release) => release.Name.Split('-')[0];
+}
- private static async Task UploadArtifactToGitHubReleaseAsync(GitHubClient client, Release release, string path)
- {
- var name = Path.GetFileName(path);
- Console.WriteLine($"Uploading {name}");
- using (var stream = File.OpenRead(path))
- {
- var upload = new ReleaseAssetUpload
- {
- FileName = name,
- ContentType = "application/octet-stream",
- RawData = stream,
- Timeout = TimeSpan.FromSeconds(100)
- };
-
- var asset = await client.Repository.Release.UploadAsset(release, upload);
- Console.WriteLine($"Uploaded {asset.Name}");
- }
- }
-
- private static async Task UploadPackageToNuGetAsync(string path, string nugetServerUrl, string nugetApiKey)
+static async Task UploadArtifactToGitHubReleaseAsync(GitHubClient client, Release release, string path)
+{
+ var name = Path.GetFileName(path);
+ Console.WriteLine($"Uploading {name}");
+ using (var stream = File.OpenRead(path))
+ {
+ var upload = new ReleaseAssetUpload
{
- string name = Path.GetFileName(path);
- Console.WriteLine($"Pushing {name}");
- await RunAsync(ToolPaths.NuGet, $"push \"{path}\" -ApiKey {nugetApiKey} -Source {nugetServerUrl} -NonInteractive -ForceEnglishOutput", noEcho: true);
- Console.WriteLine($"Pushed {name}");
- }
+ FileName = name,
+ ContentType = "application/octet-stream",
+ RawData = stream,
+ Timeout = TimeSpan.FromSeconds(100)
+ };
+
+ var asset = await client.Repository.Release.UploadAsset(release, upload);
+ Console.WriteLine($"Uploaded {asset.Name}");
+ }
+}
- private static (string repoOwner, string repoName) GetRepositoryName()
- {
- var repoNameWithOwner = GetRequiredEnvironmentVariable("APPVEYOR_REPO_NAME");
- var parts = repoNameWithOwner.Split('/');
- return (parts[0], parts[1]);
- }
+static async Task UploadPackageToNuGetAsync(string path, string nugetServerUrl, string nugetApiKey)
+{
+ string name = Path.GetFileName(path);
+ Console.WriteLine($"Pushing {name}");
+ await RunAsync(ToolPaths.NuGet, $"push \"{path}\" -ApiKey {nugetApiKey} -Source {nugetServerUrl} -NonInteractive -ForceEnglishOutput", noEcho: true);
+ Console.WriteLine($"Pushed {name}");
+}
- private static GitHubClient GetAuthenticatedGitHubClient()
- {
- var token = GitHubTokenSource.GetAccessToken();
- var credentials = new Credentials(token);
- return new GitHubClient(new ProductHeaderValue("FakeItEasy-build-scripts")) { Credentials = credentials };
- }
+static (string repoOwner, string repoName) GetRepositoryName()
+{
+ var repoNameWithOwner = GetRequiredEnvironmentVariable("APPVEYOR_REPO_NAME");
+ var parts = repoNameWithOwner.Split('/');
+ return (parts[0], parts[1]);
+}
- private static string? GetAppVeyorTagName() => Environment.GetEnvironmentVariable("APPVEYOR_REPO_TAG_NAME");
+static GitHubClient GetAuthenticatedGitHubClient()
+{
+ var token = GitHubTokenSource.GetAccessToken();
+ var credentials = new Credentials(token);
+ return new GitHubClient(new ProductHeaderValue("FakeItEasy-build-scripts")) { Credentials = credentials };
+}
- private static string GetNuGetServerUrl() => GetRequiredEnvironmentVariable("NUGET_SERVER_URL");
+static string? GetAppVeyorTagName() => Environment.GetEnvironmentVariable("APPVEYOR_REPO_TAG_NAME");
- private static string GetNuGetApiKey() => GetRequiredEnvironmentVariable("NUGET_API_KEY");
+static string GetNuGetServerUrl() => GetRequiredEnvironmentVariable("NUGET_SERVER_URL");
- private static string GetRequiredEnvironmentVariable(string key)
- {
- var environmentValue = Environment.GetEnvironmentVariable(key);
- if (string.IsNullOrEmpty(environmentValue))
- {
- throw new Exception($"Required environment variable {key} is not set. Unable to continue.");
- }
-
- return environmentValue;
- }
+static string GetNuGetApiKey() => GetRequiredEnvironmentVariable("NUGET_API_KEY");
+
+static string GetRequiredEnvironmentVariable(string key)
+{
+ var environmentValue = Environment.GetEnvironmentVariable(key);
+ if (string.IsNullOrEmpty(environmentValue))
+ {
+ throw new Exception($"Required environment variable {key} is not set. Unable to continue.");
}
+
+ return environmentValue;
}
diff --git a/FakeItEasy.PrepareRelease/GitHubHelper.cs b/FakeItEasy.PrepareRelease/GitHubHelper.cs
new file mode 100644
index 0000000..56d78bf
--- /dev/null
+++ b/FakeItEasy.PrepareRelease/GitHubHelper.cs
@@ -0,0 +1,117 @@
+namespace FakeItEasy.PrepareRelease;
+
+using Octokit;
+using static FakeItEasy.Tools.ReleaseHelpers;
+
+internal class GitHubHelper
+{
+ private readonly IGitHubClient gitHubClient;
+ private readonly string repoOwner;
+ private readonly string repoName;
+
+ public GitHubHelper(IGitHubClient gitHubClient, string repoOwner, string repoName)
+ {
+ this.gitHubClient = gitHubClient;
+ this.repoOwner = repoOwner;
+ this.repoName = repoName;
+ }
+
+ public async Task GetExistingMilestone(string existingMilestoneTitle)
+ {
+ Console.WriteLine($"Fetching milestone '{existingMilestoneTitle}'...");
+ var milestoneRequest = new MilestoneRequest { State = ItemStateFilter.Open };
+ var existingMilestone = (await this.gitHubClient.Issue.Milestone.GetAllForRepository(this.repoOwner, this.repoName, milestoneRequest))
+ .Single(milestone => milestone.Title == existingMilestoneTitle);
+ Console.WriteLine($"Fetched milestone '{existingMilestone.Title}'");
+ return existingMilestone;
+ }
+
+ public async Task> GetAllReleases()
+ {
+ Console.WriteLine("Fetching all GitHub releases...");
+ var allReleases = await this.gitHubClient.Repository.Release.GetAll(this.repoOwner, this.repoName);
+ Console.WriteLine("Fetched all GitHub releases");
+ return allReleases;
+ }
+
+ public async Task> GetIssuesInMilestone(Milestone milestone)
+ {
+ Console.WriteLine($"Fetching issues in milestone '{milestone.Title}'...'");
+ var issueRequest = new RepositoryIssueRequest { Milestone = milestone.Number.ToString(), State = ItemStateFilter.All };
+ var issues = (await this.gitHubClient.Issue.GetAllForRepository(this.repoOwner, this.repoName, issueRequest)).ToList();
+ Console.WriteLine($"Fetched {issues.Count} issues in milestone '{milestone.Title}'");
+ return issues;
+ }
+
+ public async Task RenameMilestone(Milestone existingMilestone, string version)
+ {
+ var milestoneUpdate = new MilestoneUpdate { Title = version };
+ Console.WriteLine($"Renaming milestone '{existingMilestone.Title}' to '{milestoneUpdate.Title}'...");
+ var updatedMilestone = await this.gitHubClient.Issue.Milestone.Update(this.repoOwner, this.repoName, existingMilestone.Number, milestoneUpdate);
+ Console.WriteLine($"Renamed milestone '{existingMilestone.Title}' to '{updatedMilestone.Title}'");
+ }
+
+ public async Task CreateNextMilestone(string nextReleaseName)
+ {
+ var newMilestone = new NewMilestone(nextReleaseName);
+ Console.WriteLine($"Creating new milestone '{newMilestone.Title}'...");
+ var nextMilestone = await this.gitHubClient.Issue.Milestone.Create(this.repoOwner, this.repoName, newMilestone);
+ Console.WriteLine($"Created new milestone '{nextMilestone.Title}'");
+ return nextMilestone;
+ }
+
+ public async Task UpdateRelease(Release existingRelease, string version)
+ {
+ var releaseUpdate = new ReleaseUpdate { Name = version, TagName = version, Prerelease = IsPreRelease(version) };
+ Console.WriteLine($"Renaming GitHub release '{existingRelease.Name}' to {releaseUpdate.Name}...");
+ var updatedRelease = await this.gitHubClient.Repository.Release.Edit(this.repoOwner, this.repoName, existingRelease.Id, releaseUpdate);
+ Console.WriteLine($"Renamed GitHub release '{existingRelease.Name}' to {updatedRelease.Name}");
+ }
+
+ public async Task CreateNextRelease(string nextReleaseName)
+ {
+ const string newReleaseBody = @"
+### Changed
+
+### New
+* Issue Title (#12345)
+
+### Fixed
+
+### Additional Items
+
+### With special thanks for contributions to this release from:
+* Real Name - @githubhandle
+";
+
+ var newRelease = new NewRelease(nextReleaseName) { Draft = true, Name = nextReleaseName, Body = newReleaseBody.Trim() };
+ Console.WriteLine($"Creating new GitHub release '{newRelease.Name}'...");
+ var nextRelease = await this.gitHubClient.Repository.Release.Create(this.repoOwner, this.repoName, newRelease);
+ Console.WriteLine($"Created new GitHub release '{nextRelease.Name}'");
+ }
+
+ public async Task UpdateIssue(Issue existingIssue, Milestone existingMilestone, string version)
+ {
+ var issueUpdate = new IssueUpdate { Title = $"Release {version}", Milestone = existingMilestone.Number };
+ Console.WriteLine($"Renaming release issue '{existingIssue.Title}' to '{issueUpdate.Title}'...");
+ var updatedIssue = await this.gitHubClient.Issue.Update(this.repoOwner, this.repoName, existingIssue.Number, issueUpdate);
+ Console.WriteLine($"Renamed release issue '{existingIssue.Title}' to '{updatedIssue.Title}'");
+ }
+
+ public async Task CreateNextIssue(Issue existingIssue, Milestone nextMilestone, string nextReleaseName)
+ {
+ var newIssue = new NewIssue($"Release {nextReleaseName}")
+ {
+ Milestone = nextMilestone.Number,
+ Body = existingIssue.Body.Replace("[x]", "[ ]", StringComparison.OrdinalIgnoreCase),
+ };
+ foreach (var label in existingIssue.Labels)
+ {
+ newIssue.Labels.Add(label.Name);
+ }
+
+ Console.WriteLine($"Creating new release issue '{newIssue.Title}'...");
+ var nextIssue = await this.gitHubClient.Issue.Create(this.repoOwner, this.repoName, newIssue);
+ Console.WriteLine($"Created new release issue #{nextIssue.Number}: '{newIssue.Title}'");
+ }
+}
diff --git a/FakeItEasy.PrepareRelease/Program.cs b/FakeItEasy.PrepareRelease/Program.cs
index adedfa5..e186bd6 100644
--- a/FakeItEasy.PrepareRelease/Program.cs
+++ b/FakeItEasy.PrepareRelease/Program.cs
@@ -1,263 +1,147 @@
-namespace FakeItEasy.PrepareRelease
-{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using FakeItEasy.Tools;
- using Octokit;
- using static FakeItEasy.Tools.ReleaseHelpers;
-
- internal static class Program
- {
- private const string RepoOwner = "FakeItEasy";
- private static string repoName = string.Empty;
-
- public static async Task Main(string[] args)
- {
- if (args.Length != 4 || (args[1] != "next" && args[1] != "fork"))
- {
- Console.WriteLine("Illegal arguments. Must be one of the following:");
- Console.WriteLine(" next ");
- Console.WriteLine(" fork ");
- return;
- }
-
- repoName = args[0];
- var action = args[1];
- var version = args[2];
- var existingReleaseName = args[3];
-
- var gitHubClient = GetAuthenticatedGitHubClient();
- var existingMilestone = await gitHubClient.GetExistingMilestone(existingReleaseName);
- var issuesInExistingMilestone = await gitHubClient.GetIssuesInMilestone(existingMilestone);
- var existingReleaseIssue = GetExistingReleaseIssue(issuesInExistingMilestone, existingReleaseName);
-
- if (action == "next")
- {
- var nextReleaseName = existingReleaseName;
-
- var allReleases = await gitHubClient.GetAllReleases();
- var existingRelease = allReleases.Single(release => release.Name == existingReleaseName && release.Draft);
-
- var releasesForExistingMilestone = GetReleasesForExistingMilestone(allReleases, existingRelease, version);
+using FakeItEasy.PrepareRelease;
+using FakeItEasy.Tools;
+using Octokit;
+using static FakeItEasy.Tools.ReleaseHelpers;
- var nonReleaseIssuesInMilestone = ExcludeReleaseIssues(issuesInExistingMilestone, releasesForExistingMilestone);
-
- var issueNumbersReferencedFromReleases = GetIssueNumbersReferencedFromReleases(releasesForExistingMilestone);
-
- if (!CrossReferenceIssues(nonReleaseIssuesInMilestone, issueNumbersReferencedFromReleases))
- {
- return;
- }
-
- Milestone nextMilestone;
- if (IsPreRelease(version))
- {
- nextMilestone = existingMilestone;
- }
- else
- {
- await gitHubClient.RenameMilestone(existingMilestone, version);
- nextMilestone = await gitHubClient.CreateNextMilestone(nextReleaseName);
- }
+if (args.Length != 4 || (args[1] != "next" && args[1] != "fork"))
+{
+ Console.WriteLine("Illegal arguments. Must be one of the following:");
+ Console.WriteLine(" next ");
+ Console.WriteLine(" fork ");
+ return;
+}
- await gitHubClient.UpdateRelease(existingRelease, version);
- await gitHubClient.CreateNextRelease(nextReleaseName);
- await gitHubClient.UpdateIssue(existingReleaseIssue, existingMilestone, version);
- await gitHubClient.CreateNextIssue(existingReleaseIssue, nextMilestone, nextReleaseName);
- }
- else
- {
- var nextReleaseName = version;
+const string repoOwner = "FakeItEasy";
+var repoName = args[0];
+var action = args[1];
+var version = args[2];
+var existingReleaseName = args[3];
- var nextMilestone = await gitHubClient.CreateNextMilestone(nextReleaseName);
- await gitHubClient.CreateNextRelease(nextReleaseName);
- await gitHubClient.CreateNextIssue(existingReleaseIssue, nextMilestone, nextReleaseName);
- }
- }
+var gitHubClient = GetAuthenticatedGitHubClient();
+var gitHubHelper = new GitHubHelper(gitHubClient, repoOwner, repoName);
- private static List GetReleasesForExistingMilestone(IReadOnlyCollection allReleases, Release existingRelease, string version)
- {
- var releasesForExistingMilestone = new List { existingRelease };
- var versionRoot = IsPreRelease(version) ? version.Substring(0, version.IndexOf('-', StringComparison.Ordinal)) : version;
- releasesForExistingMilestone.AddRange(allReleases.Where(release => release.Name.StartsWith(versionRoot, StringComparison.OrdinalIgnoreCase)));
- return releasesForExistingMilestone;
- }
+var existingMilestone = await gitHubHelper.GetExistingMilestone(existingReleaseName);
+var issuesInExistingMilestone = await gitHubHelper.GetIssuesInMilestone(existingMilestone);
+var existingReleaseIssue = GetExistingReleaseIssue(issuesInExistingMilestone, existingReleaseName);
- private static GitHubClient GetAuthenticatedGitHubClient()
- {
- var token = GitHubTokenSource.GetAccessToken();
- var credentials = new Credentials(token);
- return new GitHubClient(new ProductHeaderValue("FakeItEasy-build-scripts")) { Credentials = credentials };
- }
+if (action == "next")
+{
+ var nextReleaseName = existingReleaseName;
- private static async Task GetExistingMilestone(this IGitHubClient gitHubClient, string existingMilestoneTitle)
- {
- Console.WriteLine($"Fetching milestone '{existingMilestoneTitle}'...");
- var milestoneRequest = new MilestoneRequest { State = ItemStateFilter.Open };
- var existingMilestone = (await gitHubClient.Issue.Milestone.GetAllForRepository(RepoOwner, repoName, milestoneRequest))
- .Single(milestone => milestone.Title == existingMilestoneTitle);
- Console.WriteLine($"Fetched milestone '{existingMilestone.Title}'");
- return existingMilestone;
- }
+ var allReleases = await gitHubHelper.GetAllReleases();
+ var existingRelease = allReleases.Single(release => release.Name == existingReleaseName && release.Draft);
- private static async Task> GetAllReleases(this IGitHubClient gitHubClient)
- {
- Console.WriteLine("Fetching all GitHub releases...");
- var allReleases = await gitHubClient.Repository.Release.GetAll(RepoOwner, repoName);
- Console.WriteLine("Fetched all GitHub releases");
- return allReleases;
- }
+ var releasesForExistingMilestone = GetReleasesForExistingMilestone(allReleases, existingRelease, version);
- private static async Task> GetIssuesInMilestone(this IGitHubClient gitHubClient, Milestone milestone)
- {
- Console.WriteLine($"Fetching issues in milestone '{milestone.Title}'...'");
- var issueRequest = new RepositoryIssueRequest { Milestone = milestone.Number.ToString(), State = ItemStateFilter.All };
- var issues = (await gitHubClient.Issue.GetAllForRepository(RepoOwner, repoName, issueRequest)).ToList();
- Console.WriteLine($"Fetched {issues.Count} issues in milestone '{milestone.Title}'");
- return issues;
- }
+ var nonReleaseIssuesInMilestone = ExcludeReleaseIssues(issuesInExistingMilestone, releasesForExistingMilestone);
- private static Issue GetExistingReleaseIssue(IList issues, string existingReleaseName)
- {
- var issue = issues.Single(i => i.Title == $"Release {existingReleaseName}");
- Console.WriteLine($"Found release issue #{issue.Number}: '{issue.Title}'");
- return issue;
- }
+ var issueNumbersReferencedFromReleases = GetIssueNumbersReferencedFromReleases(releasesForExistingMilestone);
- private static IList ExcludeReleaseIssues(IList issues, IEnumerable releases)
- {
- return issues.Where(issue => releases.All(release => $"Release {release.Name}" != issue.Title)).ToList();
- }
-
- private static bool CrossReferenceIssues(ICollection issuesInMilestone, ICollection issueNumbersReferencedFromRelease)
- {
- var issueNumbersInMilestone = issuesInMilestone.Select(i => i.Number);
- var issueNumbersInReleaseButNotMilestone = issueNumbersReferencedFromRelease.Except(issueNumbersInMilestone).ToList();
- var issuesInMilestoneButNotRelease = issuesInMilestone.Where(i => !issueNumbersReferencedFromRelease.Contains(i.Number)).ToList();
-
- if (!issuesInMilestoneButNotRelease.Any() && !issueNumbersInReleaseButNotMilestone.Any())
- {
- Console.WriteLine("The release refers to the same issues included in the milestone. Congratulations.");
- return true;
- }
+ if (!CrossReferenceIssues(nonReleaseIssuesInMilestone, issueNumbersReferencedFromReleases))
+ {
+ return;
+ }
- Console.WriteLine();
+ Milestone nextMilestone;
+ if (IsPreRelease(version))
+ {
+ nextMilestone = existingMilestone;
+ }
+ else
+ {
+ await gitHubHelper.RenameMilestone(existingMilestone, version);
+ nextMilestone = await gitHubHelper.CreateNextMilestone(nextReleaseName);
+ }
- if (issuesInMilestoneButNotRelease.Any())
- {
- Console.WriteLine("The following issues are linked to the milestone but not referenced in the release:");
- foreach (var issue in issuesInMilestoneButNotRelease)
- {
- Console.WriteLine($" #{issue.Number}: {issue.Title}");
- }
+ await gitHubHelper.UpdateRelease(existingRelease, version);
+ await gitHubHelper.CreateNextRelease(nextReleaseName);
+ await gitHubHelper.UpdateIssue(existingReleaseIssue, existingMilestone, version);
+ await gitHubHelper.CreateNextIssue(existingReleaseIssue, nextMilestone, nextReleaseName);
+}
+else
+{
+ var nextReleaseName = version;
- Console.WriteLine();
- }
+ var nextMilestone = await gitHubHelper.CreateNextMilestone(nextReleaseName);
+ await gitHubHelper.CreateNextRelease(nextReleaseName);
+ await gitHubHelper.CreateNextIssue(existingReleaseIssue, nextMilestone, nextReleaseName);
+}
- if (issueNumbersInReleaseButNotMilestone.Any())
- {
- Console.WriteLine("The following issues are referenced in the release but not linked to the milestone:");
- foreach (var issueNumber in issueNumbersInReleaseButNotMilestone)
- {
- Console.WriteLine($" #{issueNumber}");
- }
+static List GetReleasesForExistingMilestone(IReadOnlyCollection allReleases, Release existingRelease, string version)
+{
+ var releasesForExistingMilestone = new List { existingRelease };
+ var versionRoot = IsPreRelease(version) ? version.Substring(0, version.IndexOf('-', StringComparison.Ordinal)) : version;
+ releasesForExistingMilestone.AddRange(allReleases.Where(release => release.Name.StartsWith(versionRoot, StringComparison.OrdinalIgnoreCase)));
+ return releasesForExistingMilestone;
+}
- Console.WriteLine();
- }
+static GitHubClient GetAuthenticatedGitHubClient()
+{
+ var token = GitHubTokenSource.GetAccessToken();
+ var credentials = new Credentials(token);
+ return new GitHubClient(new ProductHeaderValue("FakeItEasy-build-scripts")) { Credentials = credentials };
+}
- Console.WriteLine("Prepare release anyhow? (y/N)");
- var response = Console.ReadLine().Trim();
- if (string.Equals(response, "y", StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
+static Issue GetExistingReleaseIssue(IList issues, string existingReleaseName)
+{
+ var issue = issues.Single(i => i.Title == $"Release {existingReleaseName}");
+ Console.WriteLine($"Found release issue #{issue.Number}: '{issue.Title}'");
+ return issue;
+}
- if (string.Equals(response, "n", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
+static IList ExcludeReleaseIssues(IList issues, IEnumerable releases)
+{
+ return issues.Where(issue => releases.All(release => $"Release {release.Name}" != issue.Title)).ToList();
+}
- Console.WriteLine($"Unknown response '{response}' received. Treating as 'n'.");
- return false;
- }
+static bool CrossReferenceIssues(ICollection issuesInMilestone, ICollection issueNumbersReferencedFromRelease)
+{
+ var issueNumbersInMilestone = issuesInMilestone.Select(i => i.Number);
+ var issueNumbersInReleaseButNotMilestone = issueNumbersReferencedFromRelease.Except(issueNumbersInMilestone).ToList();
+ var issuesInMilestoneButNotRelease = issuesInMilestone.Where(i => !issueNumbersReferencedFromRelease.Contains(i.Number)).ToList();
- private static bool IsPreRelease(string version)
- {
- return version.Contains('-', StringComparison.Ordinal);
- }
+ if (!issuesInMilestoneButNotRelease.Any() && !issueNumbersInReleaseButNotMilestone.Any())
+ {
+ Console.WriteLine("The release refers to the same issues included in the milestone. Congratulations.");
+ return true;
+ }
- private static async Task RenameMilestone(this IGitHubClient gitHubClient, Milestone existingMilestone, string version)
- {
- var milestoneUpdate = new MilestoneUpdate { Title = version };
- Console.WriteLine($"Renaming milestone '{existingMilestone.Title}' to '{milestoneUpdate.Title}'...");
- var updatedMilestone = await gitHubClient.Issue.Milestone.Update(RepoOwner, repoName, existingMilestone.Number, milestoneUpdate);
- Console.WriteLine($"Renamed milestone '{existingMilestone.Title}' to '{updatedMilestone.Title}'");
- }
+ Console.WriteLine();
- private static async Task CreateNextMilestone(this IGitHubClient gitHubClient, string nextReleaseName)
+ if (issuesInMilestoneButNotRelease.Any())
+ {
+ Console.WriteLine("The following issues are linked to the milestone but not referenced in the release:");
+ foreach (var issue in issuesInMilestoneButNotRelease)
{
- var newMilestone = new NewMilestone(nextReleaseName);
- Console.WriteLine($"Creating new milestone '{newMilestone.Title}'...");
- var nextMilestone = await gitHubClient.Issue.Milestone.Create(RepoOwner, repoName, newMilestone);
- Console.WriteLine($"Created new milestone '{nextMilestone.Title}'");
- return nextMilestone;
+ Console.WriteLine($" #{issue.Number}: {issue.Title}");
}
- private static async Task UpdateRelease(this IGitHubClient gitHubClient, Release existingRelease, string version)
- {
- var releaseUpdate = new ReleaseUpdate { Name = version, TagName = version, Prerelease = IsPreRelease(version) };
- Console.WriteLine($"Renaming GitHub release '{existingRelease.Name}' to {releaseUpdate.Name}...");
- var updatedRelease = await gitHubClient.Repository.Release.Edit(RepoOwner, repoName, existingRelease.Id, releaseUpdate);
- Console.WriteLine($"Renamed GitHub release '{existingRelease.Name}' to {updatedRelease.Name}");
- }
+ Console.WriteLine();
+ }
- private static async Task CreateNextRelease(this IGitHubClient gitHubClient, string nextReleaseName)
+ if (issueNumbersInReleaseButNotMilestone.Any())
+ {
+ Console.WriteLine("The following issues are referenced in the release but not linked to the milestone:");
+ foreach (var issueNumber in issueNumbersInReleaseButNotMilestone)
{
- const string newReleaseBody = @"
-### Changed
-
-### New
-* Issue Title (#12345)
-
-### Fixed
-
-### Additional Items
-
-### With special thanks for contributions to this release from:
-* Real Name - @githubhandle
-";
-
- var newRelease = new NewRelease(nextReleaseName) { Draft = true, Name = nextReleaseName, Body = newReleaseBody.Trim() };
- Console.WriteLine($"Creating new GitHub release '{newRelease.Name}'...");
- var nextRelease = await gitHubClient.Repository.Release.Create(RepoOwner, repoName, newRelease);
- Console.WriteLine($"Created new GitHub release '{nextRelease.Name}'");
+ Console.WriteLine($" #{issueNumber}");
}
- private static async Task UpdateIssue(this IGitHubClient gitHubClient, Issue existingIssue, Milestone existingMilestone, string version)
- {
- var issueUpdate = new IssueUpdate { Title = $"Release {version}", Milestone = existingMilestone.Number };
- Console.WriteLine($"Renaming release issue '{existingIssue.Title}' to '{issueUpdate.Title}'...");
- var updatedIssue = await gitHubClient.Issue.Update(RepoOwner, repoName, existingIssue.Number, issueUpdate);
- Console.WriteLine($"Renamed release issue '{existingIssue.Title}' to '{updatedIssue.Title}'");
- }
+ Console.WriteLine();
+ }
- private static async Task CreateNextIssue(this IGitHubClient gitHubClient, Issue existingIssue, Milestone nextMilestone, string nextReleaseName)
- {
- var newIssue = new NewIssue($"Release {nextReleaseName}")
- {
- Milestone = nextMilestone.Number,
- Body = existingIssue.Body.Replace("[x]", "[ ]", StringComparison.OrdinalIgnoreCase),
- };
- foreach (var label in existingIssue.Labels)
- {
- newIssue.Labels.Add(label.Name);
- }
+ Console.WriteLine("Prepare release anyhow? (y/N)");
+ var response = Console.ReadLine()?.Trim();
+ if (string.Equals(response, "y", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
- Console.WriteLine($"Creating new release issue '{newIssue.Title}'...");
- var nextIssue = await gitHubClient.Issue.Create(RepoOwner, repoName, newIssue);
- Console.WriteLine($"Created new release issue #{nextIssue.Number}: '{newIssue.Title}'");
- }
+ if (string.Equals(response, "n", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
}
+
+ Console.WriteLine($"Unknown response '{response}' received. Treating as 'n'.");
+ return false;
}
diff --git a/GitHubTokenSource.cs b/GitHubTokenSource.cs
index 0e1da11..5916c24 100644
--- a/GitHubTokenSource.cs
+++ b/GitHubTokenSource.cs
@@ -1,30 +1,26 @@
-namespace FakeItEasy.Tools
-{
- using System;
- using System.IO;
+namespace FakeItEasy.Tools;
- using static FakeItEasy.Tools.ToolHelpers;
+using static FakeItEasy.Tools.ToolHelpers;
- public static class GitHubTokenSource
+public static class GitHubTokenSource
+{
+ public static string GetAccessToken()
{
- public static string GetAccessToken()
+ var tokenFilePath = Path.Combine(GetCurrentScriptDirectory(), ".githubtoken");
+ var token = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
+ if (string.IsNullOrEmpty(token))
{
- var tokenFilePath = Path.Combine(GetCurrentScriptDirectory(), ".githubtoken");
- var token = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
- if (string.IsNullOrEmpty(token))
- {
- if (File.Exists(tokenFilePath))
- {
- token = File.ReadAllText(tokenFilePath)?.Trim();
- }
- }
-
- if (string.IsNullOrEmpty(token))
+ if (File.Exists(tokenFilePath))
{
- throw new Exception($"GitHub access token is missing; please put it in '{tokenFilePath}', or in the GITHUB_TOKEN environment variable.");
+ token = File.ReadAllText(tokenFilePath).Trim();
}
+ }
- return token;
+ if (string.IsNullOrEmpty(token))
+ {
+ throw new Exception($"GitHub access token is missing; please put it in '{tokenFilePath}', or in the GITHUB_TOKEN environment variable.");
}
+
+ return token;
}
}
diff --git a/ReleaseHelpers.cs b/ReleaseHelpers.cs
index 1bb1528..bb2917d 100644
--- a/ReleaseHelpers.cs
+++ b/ReleaseHelpers.cs
@@ -1,31 +1,33 @@
-namespace FakeItEasy.Tools
-{
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using System.Text.RegularExpressions;
- using Octokit;
+namespace FakeItEasy.Tools;
+
+using System.Globalization;
+using System.Text.RegularExpressions;
+using Octokit;
- public static class ReleaseHelpers
+internal static class ReleaseHelpers
+{
+ public static ICollection GetIssueNumbersReferencedFromReleases(IEnumerable releases)
{
- public static ICollection GetIssueNumbersReferencedFromReleases(IEnumerable releases)
+ if (releases is null)
{
- if (releases is null)
- {
- throw new System.ArgumentNullException(nameof(releases));
- }
+ throw new ArgumentNullException(nameof(releases));
+ }
- var issuesReferencedFromRelease = new HashSet();
- foreach (var release in releases)
+ var issuesReferencedFromRelease = new HashSet();
+ foreach (var release in releases)
+ {
+ foreach (var capture in Regex.Matches(release.Body, @"\(\s*#(?[0-9]+)(,\s*#(?[0-9]+))*\s*\)")
+ .SelectMany(match => match.Groups["issueNumber"].Captures))
{
- foreach (var capture in Regex.Matches(release.Body, @"\(\s*#(?[0-9]+)(,\s*#(?[0-9]+))*\s*\)")
- .SelectMany(match => match.Groups["issueNumber"].Captures))
- {
- issuesReferencedFromRelease.Add(int.Parse(capture.Value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo));
- }
+ issuesReferencedFromRelease.Add(int.Parse(capture.Value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo));
}
-
- return issuesReferencedFromRelease;
}
+
+ return issuesReferencedFromRelease;
+ }
+
+ public static bool IsPreRelease(string version)
+ {
+ return version.Contains('-', StringComparison.Ordinal);
}
}
diff --git a/ToolHelpers.cs b/ToolHelpers.cs
index f9c6a2c..7db1ff5 100644
--- a/ToolHelpers.cs
+++ b/ToolHelpers.cs
@@ -1,12 +1,9 @@
-namespace FakeItEasy.Tools
-{
- using System;
- using System.IO;
- using System.Runtime.CompilerServices;
+namespace FakeItEasy.Tools;
+
+using System.Runtime.CompilerServices;
- public static class ToolHelpers
- {
- public static string GetCurrentScriptDirectory([CallerFilePath] string path = "") => Path.GetDirectoryName(path)
- ?? throw new Exception("Can't find current script directory.");
- }
+public static class ToolHelpers
+{
+ public static string GetCurrentScriptDirectory([CallerFilePath] string path = "") => Path.GetDirectoryName(path)
+ ?? throw new Exception("Can't find current script directory.");
}