From e2f22603edbe7ab345c4e4299b39a55870fa539f Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sun, 5 Feb 2023 11:16:51 +0100 Subject: [PATCH 1/7] (maint) Added generic Success- and FailureReporters --- Source/Cake.Recipe/Content/build.cake | 16 +++++ Source/Cake.Recipe/Content/parameters.cake | 5 ++ Source/Cake.Recipe/Content/reporters.cake | 75 ++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 Source/Cake.Recipe/Content/reporters.cake diff --git a/Source/Cake.Recipe/Content/build.cake b/Source/Cake.Recipe/Content/build.cake index f4130f78..a703e672 100644 --- a/Source/Cake.Recipe/Content/build.cake +++ b/Source/Cake.Recipe/Content/build.cake @@ -35,6 +35,14 @@ Teardown((context, buildVersion) => !BuildParameters.IsRunningIntegrationTests) { var messageArguments = BuildParameters.MessageArguments(buildVersion); + foreach(var reporter in BuildParameters.SuccessReporters) + { + if(reporter.ShouldBeUsed && reporter.CanBeUsed) + { + reporter.ReportSuccess(context, buildVersion); + } + } + if (BuildParameters.CanPostToTwitter && BuildParameters.ShouldPostToTwitter) { SendMessageToTwitter(string.Format(BuildParameters.TwitterMessage, messageArguments)); @@ -69,6 +77,14 @@ Teardown((context, buildVersion) => BuildParameters.IsMainRepository && !BuildParameters.IsRunningIntegrationTests) { + foreach(var reporter in BuildParameters.FailureReporters) + { + if(reporter.ShouldBeUsed && reporter.CanBeUsed) + { + reporter.ReportFailure(context, buildVersion, context.ThrownException); + } + } + if (BuildParameters.CanPostToSlack && BuildParameters.ShouldPostToSlack) { SendMessageToSlackChannel("Continuous Integration Build of " + BuildParameters.Title + " just failed :-("); diff --git a/Source/Cake.Recipe/Content/parameters.cake b/Source/Cake.Recipe/Content/parameters.cake index 4084c673..c8ac73c4 100644 --- a/Source/Cake.Recipe/Content/parameters.cake +++ b/Source/Cake.Recipe/Content/parameters.cake @@ -42,6 +42,8 @@ public static class BuildParameters public static bool ForceContinuousIntegration { get; private set; } public static PlatformFamily PreferredBuildAgentOperatingSystem { get; private set;} public static BuildProviderType PreferredBuildProviderType { get; private set; } + public static SuccessReporterList SuccessReporters { get; private set; } + public static FailureReporterList FailureReporters { get; private set; } public static List PackageSources { get; private set; } @@ -670,5 +672,8 @@ public static class BuildParameters PackageSources.Add(new PackageSourceData(context, "GPR", gprUrl, FeedType.NuGet, false)); } } + + SuccessReporters = new SuccessReporterList(); + FailureReporters = new FailureReporterList(); } } diff --git a/Source/Cake.Recipe/Content/reporters.cake b/Source/Cake.Recipe/Content/reporters.cake new file mode 100644 index 00000000..b229c95d --- /dev/null +++ b/Source/Cake.Recipe/Content/reporters.cake @@ -0,0 +1,75 @@ +public interface IReporterBase +{ + public abstract string Name { get; } + public abstract bool CanBeUsed { get; } + public bool ShouldBeUsed { get; set; } +} + +public interface ISuccessReporter : IReporterBase +{ + void ReportSuccess(ICakeContext context, BuildVersion buildVersion); +} + +public interface IFailureReporter : IReporterBase +{ + void ReportFailure(ICakeContext context, BuildVersion buildVersion, Exception thrownException); +} + +public abstract class SuccessReporter : ISuccessReporter +{ + public abstract string Name { get; } + public abstract bool CanBeUsed { get; } + public bool ShouldBeUsed { get; set; } + + public abstract void ReportSuccess(ICakeContext context, BuildVersion buildVersion); +} + +public abstract class FailureReporter : IFailureReporter +{ + public abstract string Name { get; } + public abstract bool CanBeUsed { get; } + public bool ShouldBeUsed { get; set; } + + public abstract void ReportFailure(ICakeContext context, BuildVersion buildVersion, Exception thrownException); +} + +public abstract class ReporterList : IEnumerable +where TReporter: IReporterBase +{ + private readonly Dictionary _backing = new Dictionary(); + + public void Add(TReporter reporter) + { + _backing.Add(reporter.Name.ToUpperInvariant(), reporter); + } + + public bool HasReporter(string name) + { + return _backing.Keys.Contains(name.ToUpperInvariant()); + } + + IEnumerator IEnumerable.GetEnumerator() + => _backing.Values.GetEnumerator(); + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + => _backing.Values.GetEnumerator(); + + public TReporter this[string name] + { + get + { + if(_backing.TryGetValue(name.ToUpperInvariant(), out var reporter)) + { + return reporter; + } + + return default(TReporter); + } + } +} + +public class SuccessReporterList : ReporterList +{} + +public class FailureReporterList : ReporterList +{} \ No newline at end of file From aa17f2be602ac1522620c2e24c7c49fdd49148cf Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sun, 5 Feb 2023 11:25:05 +0100 Subject: [PATCH 2/7] (maint) Converted sending to Twitter into SuccessReporter --- Source/Cake.Recipe/Content/parameters.cake | 33 ++++---------- Source/Cake.Recipe/Content/twitter.cake | 51 ++++++++++++++++------ 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/Source/Cake.Recipe/Content/parameters.cake b/Source/Cake.Recipe/Content/parameters.cake index c8ac73c4..a661899a 100644 --- a/Source/Cake.Recipe/Content/parameters.cake +++ b/Source/Cake.Recipe/Content/parameters.cake @@ -10,7 +10,6 @@ public enum BranchType public static class BuildParameters { private static string _microsoftTeamsMessage; - private static string _twitterMessage; private static bool _shouldUseDeterministicBuilds; public static string Target { get; private set; } @@ -67,17 +66,10 @@ public static class BuildParameters set { _microsoftTeamsMessage = value; } } - public static string TwitterMessage - { - get { return _twitterMessage ?? StandardMessage; } - set { _twitterMessage = value; } - } - public static GitHubCredentials GitHub { get; private set; } public static MicrosoftTeamsCredentials MicrosoftTeams { get; private set; } public static EmailCredentials Email { get; private set; } public static SlackCredentials Slack { get; private set; } - public static TwitterCredentials Twitter { get; private set; } public static AppVeyorCredentials AppVeyor { get; private set; } public static CodecovCredentials Codecov { get; private set; } public static CoverallsCredentials Coveralls { get; private set; } @@ -103,7 +95,6 @@ public static class BuildParameters public static bool ShouldBuildNugetSourcePackage { get; private set; } public static bool ShouldPostToSlack { get; private set; } - public static bool ShouldPostToTwitter { get; private set; } public static bool ShouldPostToMicrosoftTeams { get; private set; } public static bool ShouldSendEmail { get; private set; } public static bool ShouldDownloadMilestoneReleaseNotes { get; private set;} @@ -182,17 +173,6 @@ public static class BuildParameters } } - public static bool CanPostToTwitter - { - get - { - return !string.IsNullOrEmpty(BuildParameters.Twitter.ConsumerKey) && - !string.IsNullOrEmpty(BuildParameters.Twitter.ConsumerSecret) && - !string.IsNullOrEmpty(BuildParameters.Twitter.AccessToken) && - !string.IsNullOrEmpty(BuildParameters.Twitter.AccessTokenSecret); - } - } - public static bool CanPostToMicrosoftTeams { get @@ -269,7 +249,7 @@ public static class BuildParameters context.Information("TreatWarningsAsErrors: {0}", TreatWarningsAsErrors); context.Information("ShouldSendEmail: {0}", ShouldSendEmail); context.Information("ShouldPostToSlack: {0}", ShouldPostToSlack); - context.Information("ShouldPostToTwitter: {0}", ShouldPostToTwitter); + context.Information("ShouldPostToTwitter: {0}", SuccessReporters["Twitter"]?.ShouldBeUsed); context.Information("ShouldPostToMicrosoftTeams: {0}", ShouldPostToMicrosoftTeams); context.Information("ShouldDownloadFullReleaseNotes: {0}", ShouldDownloadFullReleaseNotes); context.Information("ShouldDownloadMilestoneReleaseNotes: {0}", ShouldDownloadMilestoneReleaseNotes); @@ -424,7 +404,6 @@ public static class BuildParameters TransifexPullPercentage = transifexPullPercentage; MicrosoftTeamsMessage = microsoftTeamsMessage; - TwitterMessage = twitterMessage; WyamRootDirectoryPath = wyamRootDirectoryPath ?? context.MakeAbsolute(context.Directory("docs")); WyamPublishDirectoryPath = wyamPublishDirectoryPath ?? context.MakeAbsolute(context.Directory("BuildArtifacts/temp/_PublishedDocumentation")); @@ -437,7 +416,6 @@ public static class BuildParameters WebBaseEditUrl = webBaseEditUrl ?? string.Format("https://github.com/{0}/{1}/tree/{2}/docs/input/", repositoryOwner, RepositoryName, developBranchName); ShouldPostToSlack = shouldPostToSlack; - ShouldPostToTwitter = shouldPostToTwitter; ShouldPostToMicrosoftTeams = shouldPostToMicrosoftTeams; ShouldSendEmail = shouldSendEmail; ShouldDownloadFullReleaseNotes = shouldDownloadFullReleaseNotes; @@ -583,7 +561,6 @@ public static class BuildParameters MicrosoftTeams = GetMicrosoftTeamsCredentials(context); Email = GetEmailCredentials(context); Slack = GetSlackCredentials(context); - Twitter = GetTwitterCredentials(context); AppVeyor = GetAppVeyorCredentials(context); Codecov = GetCodecovCredentials(context); Coveralls = GetCoverallsCredentials(context); @@ -675,5 +652,13 @@ public static class BuildParameters SuccessReporters = new SuccessReporterList(); FailureReporters = new FailureReporterList(); + SuccessReporters.Add( + new TwitterReporter( + GetTwitterCredentials(context), + twitterMessage ?? StandardMessage) + { + ShouldBeUsed = shouldPostToTwitter + } + ); } } diff --git a/Source/Cake.Recipe/Content/twitter.cake b/Source/Cake.Recipe/Content/twitter.cake index 8410c5ea..550532cd 100644 --- a/Source/Cake.Recipe/Content/twitter.cake +++ b/Source/Cake.Recipe/Content/twitter.cake @@ -1,23 +1,46 @@ -/////////////////////////////////////////////////////////////////////////////// -// HELPER METHODS -/////////////////////////////////////////////////////////////////////////////// -public void SendMessageToTwitter(string message) +public class TwitterReporter : SuccessReporter { - try + private TwitterCredentials _credentials; + private string _messageTemplate; + + public TwitterReporter(TwitterCredentials credentials, string messageTemplate) { - Information("Sending message to Twitter..."); + _credentials = credentials; + _messageTemplate = messageTemplate; + } - TwitterSendTweet(BuildParameters.Twitter.ConsumerKey, - BuildParameters.Twitter.ConsumerSecret, - BuildParameters.Twitter.AccessToken, - BuildParameters.Twitter.AccessTokenSecret, - message); + public override string Name { get; } = "Twitter"; - Information("Message successfully sent."); + public override bool CanBeUsed + { + get => !string.IsNullOrEmpty(_credentials.ConsumerKey) && + !string.IsNullOrEmpty(_credentials.ConsumerSecret) && + !string.IsNullOrEmpty(_credentials.AccessToken) && + !string.IsNullOrEmpty(_credentials.AccessTokenSecret); } - catch(Exception ex) + + + public override void ReportSuccess(ICakeContext context, BuildVersion buildVersion) { - Error("{0}", ex); + try + { + context.Information("Sending message to Twitter..."); + + var messageArguments = BuildParameters.MessageArguments(buildVersion); + var message = string.Format(_messageTemplate, messageArguments); + + context.TwitterSendTweet(_credentials.ConsumerKey, + _credentials.ConsumerSecret, + _credentials.AccessToken, + _credentials.AccessTokenSecret, + message); + + context.Information("Message successfully sent."); + } + catch(Exception ex) + { + context.Error("{0}", ex); + } } } From 7092c1743dd2ed7fdc9a66cc155abeda31da8c47 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sun, 5 Feb 2023 11:44:26 +0100 Subject: [PATCH 3/7] (maint) Converted sending to MS Teams into SuccessReporter --- .../Cake.Recipe/Content/microsoftteams.cake | 44 +++++++++++++------ Source/Cake.Recipe/Content/parameters.cake | 31 ++++--------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Source/Cake.Recipe/Content/microsoftteams.cake b/Source/Cake.Recipe/Content/microsoftteams.cake index 8ed1a342..9d91cc3b 100644 --- a/Source/Cake.Recipe/Content/microsoftteams.cake +++ b/Source/Cake.Recipe/Content/microsoftteams.cake @@ -1,21 +1,39 @@ -/////////////////////////////////////////////////////////////////////////////// -// HELPER METHODS -/////////////////////////////////////////////////////////////////////////////// - -public void SendMessageToMicrosoftTeams(string message) +public class MsTeamsReporter : SuccessReporter { - try + private MicrosoftTeamsCredentials _credentials; + private string _messageTemplate; + + public MsTeamsReporter(MicrosoftTeamsCredentials credentials, string messageTemplate) { - Information("Sending message to Microsoft Teams..."); + _credentials = credentials; + _messageTemplate = messageTemplate; + } - MicrosoftTeamsPostMessage(message, - new MicrosoftTeamsSettings { - IncomingWebhookUrl = BuildParameters.MicrosoftTeams.WebHookUrl - }); + public override string Name { get; } = "MicrosoftTeams"; + public override bool CanBeUsed + { + get => !string.IsNullOrEmpty(_credentials.WebHookUrl); } - catch(Exception ex) + + public override void ReportSuccess(ICakeContext context, BuildVersion buildVersion) { - Error("{0}", ex); + try + { + context.Information("Sending message to Microsoft Teams..."); + + var messageArguments = BuildParameters.MessageArguments(buildVersion); + var message = string.Format(_messageTemplate, messageArguments); + + context.MicrosoftTeamsPostMessage(message, + new MicrosoftTeamsSettings { + IncomingWebhookUrl = _credentials.WebHookUrl + }); + + } + catch(Exception ex) + { + context.Error("{0}", ex); + } } } diff --git a/Source/Cake.Recipe/Content/parameters.cake b/Source/Cake.Recipe/Content/parameters.cake index a661899a..9d22cc30 100644 --- a/Source/Cake.Recipe/Content/parameters.cake +++ b/Source/Cake.Recipe/Content/parameters.cake @@ -9,7 +9,6 @@ public enum BranchType public static class BuildParameters { - private static string _microsoftTeamsMessage; private static bool _shouldUseDeterministicBuilds; public static string Target { get; private set; } @@ -60,14 +59,7 @@ public static class BuildParameters get { return "Version {0} of the {1} Addin has just been released, this will be available here https://www.nuget.org/packages/{1}, once package indexing is complete."; } } - public static string MicrosoftTeamsMessage - { - get { return _microsoftTeamsMessage ?? StandardMessage; } - set { _microsoftTeamsMessage = value; } - } - public static GitHubCredentials GitHub { get; private set; } - public static MicrosoftTeamsCredentials MicrosoftTeams { get; private set; } public static EmailCredentials Email { get; private set; } public static SlackCredentials Slack { get; private set; } public static AppVeyorCredentials AppVeyor { get; private set; } @@ -95,7 +87,6 @@ public static class BuildParameters public static bool ShouldBuildNugetSourcePackage { get; private set; } public static bool ShouldPostToSlack { get; private set; } - public static bool ShouldPostToMicrosoftTeams { get; private set; } public static bool ShouldSendEmail { get; private set; } public static bool ShouldDownloadMilestoneReleaseNotes { get; private set;} public static bool ShouldDownloadFullReleaseNotes { get; private set;} @@ -173,14 +164,6 @@ public static class BuildParameters } } - public static bool CanPostToMicrosoftTeams - { - get - { - return !string.IsNullOrEmpty(BuildParameters.MicrosoftTeams.WebHookUrl); - } - } - public static bool CanUseWyam { get @@ -250,7 +233,7 @@ public static class BuildParameters context.Information("ShouldSendEmail: {0}", ShouldSendEmail); context.Information("ShouldPostToSlack: {0}", ShouldPostToSlack); context.Information("ShouldPostToTwitter: {0}", SuccessReporters["Twitter"]?.ShouldBeUsed); - context.Information("ShouldPostToMicrosoftTeams: {0}", ShouldPostToMicrosoftTeams); + context.Information("ShouldPostToMicrosoftTeams: {0}", SuccessReporters["MicrosoftTeams"]?.ShouldBeUsed); context.Information("ShouldDownloadFullReleaseNotes: {0}", ShouldDownloadFullReleaseNotes); context.Information("ShouldDownloadMilestoneReleaseNotes: {0}", ShouldDownloadMilestoneReleaseNotes); context.Information("ShouldNotifyBetaReleases: {0}", ShouldNotifyBetaReleases); @@ -403,8 +386,6 @@ public static class BuildParameters TransifexPullMode = transifexPullMode; TransifexPullPercentage = transifexPullPercentage; - MicrosoftTeamsMessage = microsoftTeamsMessage; - WyamRootDirectoryPath = wyamRootDirectoryPath ?? context.MakeAbsolute(context.Directory("docs")); WyamPublishDirectoryPath = wyamPublishDirectoryPath ?? context.MakeAbsolute(context.Directory("BuildArtifacts/temp/_PublishedDocumentation")); WyamConfigurationFile = wyamConfigurationFile ?? context.MakeAbsolute((FilePath)"config.wyam"); @@ -416,7 +397,6 @@ public static class BuildParameters WebBaseEditUrl = webBaseEditUrl ?? string.Format("https://github.com/{0}/{1}/tree/{2}/docs/input/", repositoryOwner, RepositoryName, developBranchName); ShouldPostToSlack = shouldPostToSlack; - ShouldPostToMicrosoftTeams = shouldPostToMicrosoftTeams; ShouldSendEmail = shouldSendEmail; ShouldDownloadFullReleaseNotes = shouldDownloadFullReleaseNotes; ShouldDownloadMilestoneReleaseNotes = shouldDownloadMilestoneReleaseNotes; @@ -558,7 +538,6 @@ public static class BuildParameters TreatWarningsAsErrors = treatWarningsAsErrors; GitHub = GetGitHubCredentials(context); - MicrosoftTeams = GetMicrosoftTeamsCredentials(context); Email = GetEmailCredentials(context); Slack = GetSlackCredentials(context); AppVeyor = GetAppVeyorCredentials(context); @@ -660,5 +639,13 @@ public static class BuildParameters ShouldBeUsed = shouldPostToTwitter } ); + SuccessReporters.Add( + new MsTeamsReporter( + GetMicrosoftTeamsCredentials(context), + microsoftTeamsMessage ?? StandardMessage) + { + ShouldBeUsed = shouldPostToMicrosoftTeams + } + ); } } From 2060e13cca29ed74f38a3795153f3e46aa249a91 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sun, 5 Feb 2023 11:48:14 +0100 Subject: [PATCH 4/7] (maint) Converted sending mails into Succes- & FailureReporter --- Source/Cake.Recipe/Content/build.cake | 34 ------ Source/Cake.Recipe/Content/email.cake | 117 ++++++++++++++------- Source/Cake.Recipe/Content/parameters.cake | 20 ++-- 3 files changed, 87 insertions(+), 84 deletions(-) diff --git a/Source/Cake.Recipe/Content/build.cake b/Source/Cake.Recipe/Content/build.cake index a703e672..aca1594e 100644 --- a/Source/Cake.Recipe/Content/build.cake +++ b/Source/Cake.Recipe/Content/build.cake @@ -42,32 +42,6 @@ Teardown((context, buildVersion) => reporter.ReportSuccess(context, buildVersion); } } - - if (BuildParameters.CanPostToTwitter && BuildParameters.ShouldPostToTwitter) - { - SendMessageToTwitter(string.Format(BuildParameters.TwitterMessage, messageArguments)); - } - - if (BuildParameters.CanPostToMicrosoftTeams && BuildParameters.ShouldPostToMicrosoftTeams) - { - SendMessageToMicrosoftTeams(string.Format(BuildParameters.MicrosoftTeamsMessage, messageArguments)); - } - - if (BuildParameters.CanSendEmail && BuildParameters.ShouldSendEmail && !string.IsNullOrEmpty(BuildParameters.EmailRecipient)) - { - var subject = $"Continuous Integration Build of {BuildParameters.Title} completed successfully"; - var message = new StringBuilder(); - message.AppendLine(string.Format(BuildParameters.StandardMessage, messageArguments) + "
"); - message.AppendLine("
"); - message.AppendLine($"Name: {BuildParameters.Title}
"); - message.AppendLine($"Version: {buildVersion.SemVersion}
"); - message.AppendLine($"Configuration: {BuildParameters.Configuration}
"); - message.AppendLine($"Target: {BuildParameters.Target}
"); - message.AppendLine($"Cake version: {buildVersion.CakeVersion}
"); - message.AppendLine($"Cake.Recipe version: {BuildMetaData.Version}
"); - - SendEmail(subject, message.ToString(), BuildParameters.EmailRecipient, BuildParameters.EmailSenderName, BuildParameters.EmailSenderAddress); - } } } @@ -89,14 +63,6 @@ Teardown((context, buildVersion) => { SendMessageToSlackChannel("Continuous Integration Build of " + BuildParameters.Title + " just failed :-("); } - - if (BuildParameters.CanSendEmail && BuildParameters.ShouldSendEmail && !string.IsNullOrEmpty(BuildParameters.EmailRecipient)) - { - var subject = $"Continuous Integration Build of {BuildParameters.Title} failed"; - var message = context.ThrownException.ToString().Replace(System.Environment.NewLine, "
"); - - SendEmail(subject, message, BuildParameters.EmailRecipient, BuildParameters.EmailSenderName, BuildParameters.EmailSenderAddress); - } } } diff --git a/Source/Cake.Recipe/Content/email.cake b/Source/Cake.Recipe/Content/email.cake index db22184e..1f0627b7 100644 --- a/Source/Cake.Recipe/Content/email.cake +++ b/Source/Cake.Recipe/Content/email.cake @@ -1,50 +1,91 @@ using Cake.Email.Common; -/////////////////////////////////////////////////////////////////////////////// -// HELPER METHODS -/////////////////////////////////////////////////////////////////////////////// - -public void SendEmail(string subject, string message, string recipient, string senderName, string senderAddress) +public class EmailReporter : ISuccessReporter, IFailureReporter { - Information("Sending email..."); + private EmailCredentials _credentials; + + public EmailReporter(EmailCredentials credentials) + { + _credentials = credentials; + } - // The recipient parameter can contain a single email address or a comma/semi-colon separated list of email addresses - var recipients = recipient - .Split(new[] { ',', ';' }, StringSplitOptions.None) - .Select(emailAddress => new MailAddress(emailAddress)) - .ToArray(); + public string Name { get; } = "EMail"; - try + public bool CanBeUsed { - var result = Email.SendEmail( - senderName: senderName, - senderAddress: senderAddress, - recipients: recipients, - subject: subject, - htmlContent: message, - textContent: null, - attachments: null, - settings: new EmailSettings - { - SmtpHost = BuildParameters.Email.SmtpHost, - Port = BuildParameters.Email.Port, - EnableSsl = BuildParameters.Email.EnableSsl, - Username = BuildParameters.Email.Username, - Password = BuildParameters.Email.Password - } - ); + get => !string.IsNullOrEmpty(_credentials.SmtpHost) + && !string.IsNullOrEmpty(BuildParameters.EmailRecipient); + } + + public bool ShouldBeUsed { get; set; } + + public void ReportSuccess(ICakeContext context, BuildVersion buildVersion) + { + var subject = $"Continuous Integration Build of {BuildParameters.Title} completed successfully"; + var messageArguments = BuildParameters.MessageArguments(buildVersion); + var message = new StringBuilder(); + message.AppendLine(string.Format(BuildParameters.StandardMessage, messageArguments) + "
"); + message.AppendLine("
"); + message.AppendLine($"Name: {BuildParameters.Title}
"); + message.AppendLine($"Version: {buildVersion.SemVersion}
"); + message.AppendLine($"Configuration: {BuildParameters.Configuration}
"); + message.AppendLine($"Target: {BuildParameters.Target}
"); + message.AppendLine($"Cake version: {buildVersion.CakeVersion}
"); + message.AppendLine($"Cake.Recipe version: {BuildMetaData.Version}
"); + + SendEmail(context, subject, message.ToString(), BuildParameters.EmailRecipient, BuildParameters.EmailSenderName, BuildParameters.EmailSenderAddress); + } + + public void ReportFailure(ICakeContext context, BuildVersion _, Exception thrownException) + { + var subject = $"Continuous Integration Build of {BuildParameters.Title} failed"; + var message = thrownException.ToString().Replace(System.Environment.NewLine, "
"); + + SendEmail(context, subject, message, BuildParameters.EmailRecipient, BuildParameters.EmailSenderName, BuildParameters.EmailSenderAddress); + } - if (result.Ok) + private void SendEmail(ICakeContext context, string subject, string message, string recipient, string senderName, string senderAddress) + { + context.Information("Sending email..."); + + // The recipient parameter can contain a single email address or a comma/semi-colon separated list of email addresses + var recipients = recipient + .Split(new[] { ',', ';' }, StringSplitOptions.None) + .Select(emailAddress => new MailAddress(emailAddress)) + .ToArray(); + + try { - Information("Email successfully sent"); + var result = context.Email().SendEmail( + senderName: senderName, + senderAddress: senderAddress, + recipients: recipients, + subject: subject, + htmlContent: message, + textContent: null, + attachments: null, + settings: new EmailSettings + { + SmtpHost = _credentials.SmtpHost, + Port = _credentials.Port, + EnableSsl = _credentials.EnableSsl, + Username = _credentials.Username, + Password = _credentials.Password + } + ); + + if (result.Ok) + { + context.Information("Email successfully sent"); + } + else + { + context.Error("Failed to send email: {0}", result.Error); + } } - else + catch(Exception ex) { - Error("Failed to send email: {0}", result.Error); + context.Error("{0}", ex); } } - catch(Exception ex) - { - Error("{0}", ex); - } -} \ No newline at end of file +} diff --git a/Source/Cake.Recipe/Content/parameters.cake b/Source/Cake.Recipe/Content/parameters.cake index 9d22cc30..1e9251a3 100644 --- a/Source/Cake.Recipe/Content/parameters.cake +++ b/Source/Cake.Recipe/Content/parameters.cake @@ -60,7 +60,6 @@ public static class BuildParameters } public static GitHubCredentials GitHub { get; private set; } - public static EmailCredentials Email { get; private set; } public static SlackCredentials Slack { get; private set; } public static AppVeyorCredentials AppVeyor { get; private set; } public static CodecovCredentials Codecov { get; private set; } @@ -87,7 +86,6 @@ public static class BuildParameters public static bool ShouldBuildNugetSourcePackage { get; private set; } public static bool ShouldPostToSlack { get; private set; } - public static bool ShouldSendEmail { get; private set; } public static bool ShouldDownloadMilestoneReleaseNotes { get; private set;} public static bool ShouldDownloadFullReleaseNotes { get; private set;} public static bool ShouldNotifyBetaReleases { get; private set; } @@ -147,14 +145,6 @@ public static class BuildParameters } } - public static bool CanSendEmail - { - get - { - return !string.IsNullOrEmpty(BuildParameters.Email.SmtpHost); - } - } - public static bool CanPostToSlack { get @@ -397,7 +387,6 @@ public static class BuildParameters WebBaseEditUrl = webBaseEditUrl ?? string.Format("https://github.com/{0}/{1}/tree/{2}/docs/input/", repositoryOwner, RepositoryName, developBranchName); ShouldPostToSlack = shouldPostToSlack; - ShouldSendEmail = shouldSendEmail; ShouldDownloadFullReleaseNotes = shouldDownloadFullReleaseNotes; ShouldDownloadMilestoneReleaseNotes = shouldDownloadMilestoneReleaseNotes; ShouldNotifyBetaReleases = shouldNotifyBetaReleases; @@ -538,7 +527,6 @@ public static class BuildParameters TreatWarningsAsErrors = treatWarningsAsErrors; GitHub = GetGitHubCredentials(context); - Email = GetEmailCredentials(context); Slack = GetSlackCredentials(context); AppVeyor = GetAppVeyorCredentials(context); Codecov = GetCodecovCredentials(context); @@ -647,5 +635,13 @@ public static class BuildParameters ShouldBeUsed = shouldPostToMicrosoftTeams } ); + + var eMailReporter = new EmailReporter( + GetEmailCredentials(context)) + { + ShouldBeUsed = shouldSendEmail + }; + SuccessReporters.Add(eMailReporter); + FailureReporters.Add(eMailReporter); } } From 81c93b6af45ea13f85c5d5d73c55e0c574a2c28c Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sun, 5 Feb 2023 12:06:23 +0100 Subject: [PATCH 5/7] (maint) Converted sending to Slack into FailureReporter --- Source/Cake.Recipe/Content/build.cake | 5 -- Source/Cake.Recipe/Content/parameters.cake | 22 ++++----- Source/Cake.Recipe/Content/slack.cake | 55 ++++++++++++++-------- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/Source/Cake.Recipe/Content/build.cake b/Source/Cake.Recipe/Content/build.cake index aca1594e..ab7d6b96 100644 --- a/Source/Cake.Recipe/Content/build.cake +++ b/Source/Cake.Recipe/Content/build.cake @@ -58,11 +58,6 @@ Teardown((context, buildVersion) => reporter.ReportFailure(context, buildVersion, context.ThrownException); } } - - if (BuildParameters.CanPostToSlack && BuildParameters.ShouldPostToSlack) - { - SendMessageToSlackChannel("Continuous Integration Build of " + BuildParameters.Title + " just failed :-("); - } } } diff --git a/Source/Cake.Recipe/Content/parameters.cake b/Source/Cake.Recipe/Content/parameters.cake index 1e9251a3..c681b666 100644 --- a/Source/Cake.Recipe/Content/parameters.cake +++ b/Source/Cake.Recipe/Content/parameters.cake @@ -60,7 +60,6 @@ public static class BuildParameters } public static GitHubCredentials GitHub { get; private set; } - public static SlackCredentials Slack { get; private set; } public static AppVeyorCredentials AppVeyor { get; private set; } public static CodecovCredentials Codecov { get; private set; } public static CoverallsCredentials Coveralls { get; private set; } @@ -85,7 +84,6 @@ public static class BuildParameters public static int TransifexPullPercentage { get; private set; } public static bool ShouldBuildNugetSourcePackage { get; private set; } - public static bool ShouldPostToSlack { get; private set; } public static bool ShouldDownloadMilestoneReleaseNotes { get; private set;} public static bool ShouldDownloadFullReleaseNotes { get; private set;} public static bool ShouldNotifyBetaReleases { get; private set; } @@ -145,15 +143,6 @@ public static class BuildParameters } } - public static bool CanPostToSlack - { - get - { - return !string.IsNullOrEmpty(BuildParameters.Slack.Token) && - !string.IsNullOrEmpty(BuildParameters.Slack.Channel); - } - } - public static bool CanUseWyam { get @@ -220,7 +209,7 @@ public static class BuildParameters context.Information("IsTagged: {0}", IsTagged); context.Information("BranchType: {0}", BranchType); context.Information("TreatWarningsAsErrors: {0}", TreatWarningsAsErrors); - context.Information("ShouldSendEmail: {0}", ShouldSendEmail); + context.Information("ShouldSendEmail: {0}", SuccessReporters["EMail"]?.ShouldBeUsed); context.Information("ShouldPostToSlack: {0}", ShouldPostToSlack); context.Information("ShouldPostToTwitter: {0}", SuccessReporters["Twitter"]?.ShouldBeUsed); context.Information("ShouldPostToMicrosoftTeams: {0}", SuccessReporters["MicrosoftTeams"]?.ShouldBeUsed); @@ -386,7 +375,6 @@ public static class BuildParameters WebLinkRoot = webLinkRoot ?? RepositoryName; WebBaseEditUrl = webBaseEditUrl ?? string.Format("https://github.com/{0}/{1}/tree/{2}/docs/input/", repositoryOwner, RepositoryName, developBranchName); - ShouldPostToSlack = shouldPostToSlack; ShouldDownloadFullReleaseNotes = shouldDownloadFullReleaseNotes; ShouldDownloadMilestoneReleaseNotes = shouldDownloadMilestoneReleaseNotes; ShouldNotifyBetaReleases = shouldNotifyBetaReleases; @@ -527,7 +515,6 @@ public static class BuildParameters TreatWarningsAsErrors = treatWarningsAsErrors; GitHub = GetGitHubCredentials(context); - Slack = GetSlackCredentials(context); AppVeyor = GetAppVeyorCredentials(context); Codecov = GetCodecovCredentials(context); Coveralls = GetCoverallsCredentials(context); @@ -635,6 +622,13 @@ public static class BuildParameters ShouldBeUsed = shouldPostToMicrosoftTeams } ); + FailureReporters.Add( + new SlackReporter( + GetSlackCredentials(context)) + { + ShouldBeUsed = shouldPostToSlack + } + ); var eMailReporter = new EmailReporter( GetEmailCredentials(context)) diff --git a/Source/Cake.Recipe/Content/slack.cake b/Source/Cake.Recipe/Content/slack.cake index c14e73b5..b819d945 100644 --- a/Source/Cake.Recipe/Content/slack.cake +++ b/Source/Cake.Recipe/Content/slack.cake @@ -1,30 +1,47 @@ -/////////////////////////////////////////////////////////////////////////////// -// HELPER METHODS -/////////////////////////////////////////////////////////////////////////////// -public void SendMessageToSlackChannel(string message) +public class SlackReporter : FailureReporter { - try + private SlackCredentials _credentials; + + public SlackReporter(SlackCredentials credentials) + { + _credentials = credentials; + } + + public override string Name { get; } = "Slack"; + + public override bool CanBeUsed { - Information("Sending message to Slack..."); + get => !string.IsNullOrEmpty(_credentials.Token) && + !string.IsNullOrEmpty(_credentials.Channel); + } - var postMessageResult = Slack.Chat.PostMessage( - token: BuildParameters.Slack.Token, - channel: BuildParameters.Slack.Channel, - text: message - ); - if (postMessageResult.Ok) + public override void ReportFailure(ICakeContext context, BuildVersion _, Exception __) + { + try { - Information("Message {0} successfully sent", postMessageResult.TimeStamp); + context.Information("Sending message to Slack..."); + + var postMessageResult = context.Slack().Chat.PostMessage( + token: _credentials.Token, + channel: _credentials.Channel, + text: "Continuous Integration Build of " + BuildParameters.Title + " just failed :-(" + ); + + if (postMessageResult.Ok) + { + context.Information("Message {0} successfully sent", postMessageResult.TimeStamp); + } + else + { + context.Error("Failed to send message: {0}", postMessageResult.Error); + } } - else + catch(Exception ex) { - Error("Failed to send message: {0}", postMessageResult.Error); + context.Error("{0}", ex); } } - catch(Exception ex) - { - Error("{0}", ex); - } } + From 2e901255455a269b50634cc78d7e3185a94b877f Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sun, 5 Feb 2023 23:59:25 +0100 Subject: [PATCH 6/7] (#510) post to mastodon on successful publishing --- Source/Cake.Recipe/Content/addins.cake | 1 + Source/Cake.Recipe/Content/credentials.cake | 19 ++++++++ Source/Cake.Recipe/Content/environment.cake | 8 +++- Source/Cake.Recipe/Content/mastodon.cake | 43 +++++++++++++++++++ Source/Cake.Recipe/Content/parameters.cake | 12 +++++- .../fundamentals/environment-variables.md | 18 ++++++++ .../input/docs/fundamentals/set-parameters.md | 23 ++++++++++ .../docs/fundamentals/set-variable-names.md | 8 ++++ 8 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 Source/Cake.Recipe/Content/mastodon.cake diff --git a/Source/Cake.Recipe/Content/addins.cake b/Source/Cake.Recipe/Content/addins.cake index 8c1ba6a3..f7bf12e3 100644 --- a/Source/Cake.Recipe/Content/addins.cake +++ b/Source/Cake.Recipe/Content/addins.cake @@ -14,6 +14,7 @@ #addin nuget:?package=Cake.Transifex&version=1.0.1 #addin nuget:?package=Cake.Twitter&version=2.0.0 #addin nuget:?package=Cake.Wyam&version=2.2.13 +#addin nuget:?package=Cake.Mastodon&version=1.1.0 #load nuget:?package=Cake.Issues.Recipe&version=1.3.2 diff --git a/Source/Cake.Recipe/Content/credentials.cake b/Source/Cake.Recipe/Content/credentials.cake index 7f924c7a..7bfc94ae 100644 --- a/Source/Cake.Recipe/Content/credentials.cake +++ b/Source/Cake.Recipe/Content/credentials.cake @@ -136,6 +136,18 @@ public class WyamCredentials } } +public class MastodonCredentials +{ + public string AccessToken { get; private set; } + public string InstanceUrl { get; private set; } + + public MastodonCredentials(string accessToken, string instanceUrl) + { + AccessToken = accessToken; + InstanceUrl = instanceUrl; + } +} + public static GitHubCredentials GetGitHubCredentials(ICakeContext context) { string token = null; @@ -224,3 +236,10 @@ public static WyamCredentials GetWyamCredentials(ICakeContext context) context.EnvironmentVariable(Environment.WyamDeployRemoteVariable), context.EnvironmentVariable(Environment.WyamDeployBranchVariable)); } + +public static MastodonCredentials GetMastodonCredentials(ICakeContext context) +{ + return new MastodonCredentials( + context.EnvironmentVariable(Environment.MastodonAccessTokenVariable), + context.EnvironmentVariable(Environment.MastodonInstanceUrlVariable)); +} \ No newline at end of file diff --git a/Source/Cake.Recipe/Content/environment.cake b/Source/Cake.Recipe/Content/environment.cake index e6d40d41..bc13976a 100644 --- a/Source/Cake.Recipe/Content/environment.cake +++ b/Source/Cake.Recipe/Content/environment.cake @@ -20,6 +20,8 @@ public static class Environment public static string WyamAccessTokenVariable { get; private set; } public static string WyamDeployRemoteVariable { get; private set; } public static string WyamDeployBranchVariable { get; private set; } + public static string MastodonAccessTokenVariable { get; private set; } + public static string MastodonInstanceUrlVariable { get; private set; } public static void SetVariableNames( string githubTokenVariable = null, @@ -41,7 +43,9 @@ public static class Environment string transifexApiTokenVariable = null, string wyamAccessTokenVariable = null, string wyamDeployRemoteVariable = null, - string wyamDeployBranchVariable = null) + string wyamDeployBranchVariable = null, + string mastodonAccessTokenVariable = null, + string mastodonInstanceUrlVariable = null) { GithubTokenVariable = githubTokenVariable ?? "GITHUB_PAT"; SlackTokenVariable = slackTokenVariable ?? "SLACK_TOKEN"; @@ -63,5 +67,7 @@ public static class Environment WyamAccessTokenVariable = wyamAccessTokenVariable ?? "WYAM_ACCESS_TOKEN"; WyamDeployRemoteVariable = wyamDeployRemoteVariable ?? "WYAM_DEPLOY_REMOTE"; WyamDeployBranchVariable = wyamDeployBranchVariable ?? "WYAM_DEPLOY_BRANCH"; + MastodonAccessTokenVariable = mastodonAccessTokenVariable ?? "MASTODON_ACCESS_TOKEN"; + MastodonInstanceUrlVariable = mastodonInstanceUrlVariable ?? "MASTODON_INSTANCE_URL"; } } diff --git a/Source/Cake.Recipe/Content/mastodon.cake b/Source/Cake.Recipe/Content/mastodon.cake new file mode 100644 index 00000000..39be965b --- /dev/null +++ b/Source/Cake.Recipe/Content/mastodon.cake @@ -0,0 +1,43 @@ +public class MastodonReporter : SuccessReporter +{ + private MastodonCredentials _credentials; + private string _messageTemplate; + + public MastodonReporter(MastodonCredentials credentials, string messageTemplate) + { + _credentials = credentials; + _messageTemplate = messageTemplate; + } + + public override string Name { get; } = "Mastodon"; + + public override bool CanBeUsed + { + get => !string.IsNullOrEmpty(_credentials.AccessToken) && + !string.IsNullOrEmpty(_credentials.InstanceUrl); + } + + + public override void ReportSuccess(ICakeContext context, BuildVersion buildVersion) + { + try + { + context.Information("Sending message to Mastodon..."); + + var messageArguments = BuildParameters.MessageArguments(buildVersion); + var message = string.Format(_messageTemplate, messageArguments); + var idempotencyKey = Guid.NewGuid().ToString("d"); + + context.MastodonSendToot(_credentials.InstanceUrl, + _credentials.AccessToken, + message, + idempotencyKey); + + context.Information("Message successfully sent."); + } + catch(Exception ex) + { + context.Error("{0}", ex); + } + } +} diff --git a/Source/Cake.Recipe/Content/parameters.cake b/Source/Cake.Recipe/Content/parameters.cake index c681b666..45443a29 100644 --- a/Source/Cake.Recipe/Content/parameters.cake +++ b/Source/Cake.Recipe/Content/parameters.cake @@ -326,7 +326,9 @@ public static class BuildParameters List packageSourceDatas = null, PlatformFamily preferredBuildAgentOperatingSystem = PlatformFamily.Windows, BuildProviderType preferredBuildProviderType = BuildProviderType.AppVeyor, - Func messageArguments = null + Func messageArguments = null, + string mastodonMessage = null, + bool shouldPostToMastodon = true ) { if (context == null) @@ -629,6 +631,14 @@ public static class BuildParameters ShouldBeUsed = shouldPostToSlack } ); + SuccessReporters.Add( + new MastodonReporter( + GetMastodonCredentials(context), + mastodonMessage ?? StandardMessage) + { + ShouldBeUsed = shouldPostToMastodon + } + ); var eMailReporter = new EmailReporter( GetEmailCredentials(context)) diff --git a/docs/input/docs/fundamentals/environment-variables.md b/docs/input/docs/fundamentals/environment-variables.md index fc362909..1d43cf35 100644 --- a/docs/input/docs/fundamentals/environment-variables.md +++ b/docs/input/docs/fundamentals/environment-variables.md @@ -130,6 +130,24 @@ The username that should be used for authenticating to the SMTP server. The password that should be used for authenticating to the SMTP server. +## Mastodon + +When a successful release build has been completed, Cake.Recipe can be configured to send out a notification (with configurable message) to the fediverse via Mastodon. There are two required environment variables that needs to be set to make this happen. Further information about find this information can be found in the Cake.Mastodon [documentation](https://github.com/cake-contrib/Cake.Mastodon/blob/master/README.md#usage). + +:::{.alert .alert-info} +**NOTE:** + +In addition to these environment variables being present, and correct, the control variable [shouldPostToMastodon](./set-parameters#shouldPostToMastodon) also needs to be set to true. The default value for this parameter is true. +::: + +### MASTODON_ACCESS_TOKEN + +The Access Token for the Mastodon application that is going to be used to send the toot. + +### MASTODON_INSTANCE_URL + +The URL to the Mastodon instance, where the application was registered. + ## AppVeyor More information about what this is used for can be found in the [clean AppVeyor build cache](../usage/cleaning-cache) documentation. diff --git a/docs/input/docs/fundamentals/set-parameters.md b/docs/input/docs/fundamentals/set-parameters.md index 3b2c614d..74ce6788 100644 --- a/docs/input/docs/fundamentals/set-parameters.md +++ b/docs/input/docs/fundamentals/set-parameters.md @@ -225,6 +225,17 @@ Type: `bool` Default Value: +```csharp +true +``` +### shouldPostToMastodon + +This is used as a final control variable for whether or not notification messages should be posted to the fediverse via Mastodon when the a final release build (i.e. a tagged build) completes. + +Type: `bool` + +Default Value: + ```csharp true ``` @@ -518,6 +529,18 @@ Default Value: Version {0} of the {1} Addin has just been released, this will be available here https://www.nuget.org/packages/{1}, once package indexing is complete." ``` +### mastodonMessage + +This is the message that is sent to the fediverse via Mastodon at the end of a tagged build. This is formatted with the calculated version number, as well as the Title parameter. + +Type: `string` + +Default Value: + +```csharp +Version {0} of the {1} Addin has just been released, this will be available here https://www.nuget.org/packages/{1}, once package indexing is complete." +``` + ### wyamRootDirectoryPath This is the directory that stores the documentation files that will be passed to the Wyam tool. diff --git a/docs/input/docs/fundamentals/set-variable-names.md b/docs/input/docs/fundamentals/set-variable-names.md index 661ec717..9f6e869b 100644 --- a/docs/input/docs/fundamentals/set-variable-names.md +++ b/docs/input/docs/fundamentals/set-variable-names.md @@ -83,6 +83,14 @@ Default value: `EMAIL_USERNAME` Default value: `EMAIL_PASSWORD` +### mastodonAccessTokenVariable + +Default value: `MASTODON_ACCESS_TOKEN` + +### mastodonInstanceUrlVariable + +Default value: `MASTODON_INSTANCE_URL` + ### appVeyorApiTokenVariable Default value: `APPVEYOR_API_TOKEN` From b293d0a28b27b3f380ceed957fe94726d580d264 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Thu, 1 Aug 2024 21:23:09 +0100 Subject: [PATCH 7/7] (maint) Fix result of merge conflict --- Source/Cake.Recipe/Content/parameters.cake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/Cake.Recipe/Content/parameters.cake b/Source/Cake.Recipe/Content/parameters.cake index 45443a29..d41a6668 100644 --- a/Source/Cake.Recipe/Content/parameters.cake +++ b/Source/Cake.Recipe/Content/parameters.cake @@ -209,10 +209,6 @@ public static class BuildParameters context.Information("IsTagged: {0}", IsTagged); context.Information("BranchType: {0}", BranchType); context.Information("TreatWarningsAsErrors: {0}", TreatWarningsAsErrors); - context.Information("ShouldSendEmail: {0}", SuccessReporters["EMail"]?.ShouldBeUsed); - context.Information("ShouldPostToSlack: {0}", ShouldPostToSlack); - context.Information("ShouldPostToTwitter: {0}", SuccessReporters["Twitter"]?.ShouldBeUsed); - context.Information("ShouldPostToMicrosoftTeams: {0}", SuccessReporters["MicrosoftTeams"]?.ShouldBeUsed); context.Information("ShouldDownloadFullReleaseNotes: {0}", ShouldDownloadFullReleaseNotes); context.Information("ShouldDownloadMilestoneReleaseNotes: {0}", ShouldDownloadMilestoneReleaseNotes); context.Information("ShouldNotifyBetaReleases: {0}", ShouldNotifyBetaReleases);