webhooks/NetlifyHook.cs (75 lines of code) (raw):

using System.Configuration; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using Octokit; namespace React.Site.Hooks { /// <summary> /// Webhook for Netlify website builds /// </summary> public static class NetlifyHook { [FunctionName("NetlifyHook")] public static async Task<object> Run( [HttpTrigger(AuthorizationLevel.Function, WebHookType = "genericJson")]HttpRequestMessage req, TraceWriter log) { log.Info("Netlify webhook triggered"); string jsonContent = await req.Content.ReadAsStringAsync(); var data = NetlifyHookBody.CreateFromRequest(jsonContent); if (!data.ReviewId.HasValue) { log.Info("Build is not for a PR. Ignoring."); return req.CreateResponse(HttpStatusCode.NoContent); } var client = new GitHubClient(new ProductHeaderValue("reactjs-net-webhooks")) { Credentials = new Credentials(ConfigurationManager.AppSettings["GitHubToken"]) }; // Determine if the PR modified any site files var githubOwner = ConfigurationManager.AppSettings["GitHubOwner"]; var githubRepo = ConfigurationManager.AppSettings["GitHubRepo"]; var pullRequestFiles = await client.PullRequest.Files(githubOwner, githubRepo, data.ReviewId.Value); if (!pullRequestFiles.Any(IsWebsiteFile)) { log.Info("PR did not modify the site. Ignoring."); return req.CreateResponse(HttpStatusCode.NoContent); } var body = BuildCommentBody(data); // Check if a comment already exists var existingComments = await client.Issue.Comment.GetAllForIssue(githubOwner, githubRepo, data.ReviewId.Value); var existingComment = existingComments.FirstOrDefault( comment => comment.User.Login == ConfigurationManager.AppSettings["GitHubBotUser"] ); if (existingComment != null) { // There's already an existing comment, so we'll just edit that one // rather than posting a brand new comment. await client.Issue.Comment.Update(githubOwner, githubRepo, existingComment.Id, body); log.Info($"Updated existing commment {existingComment.Id}"); } else { await client.Issue.Comment.Create(githubOwner, githubRepo, data.ReviewId.Value, body); log.Info("Posted commment"); } return req.CreateResponse(HttpStatusCode.NoContent); } private static bool IsWebsiteFile(PullRequestFile file) { return file.FileName.StartsWith("site/"); } private static string BuildCommentBody(NetlifyHookBody data) { switch (data.State) { case "ready": return $"Website preview is ready!\nBuilt with commit {data.CommitRef}\n{data.DeploySslUrl}"; case "error": var netlifySite = ConfigurationManager.AppSettings["NetlifySite"]; var buildUrl = $"https://app.netlify.com/sites/{netlifySite}/deploys/{data.BuildId}"; return $"Failed to build the site :(\n\n```\n{data.ErrorMessage}\n```\n\nBuild log: {buildUrl}"; default: return $"Unknown build state: {data.State}\n{data.DeploySslUrl}"; } } } }