public bool DetermineFailure() { Utilities.WriteDebug($"Determining failure for {Repo}...", Log, LogLevel); if (BuildBreakdowns.Count == 0) { Utilities.WriteDebug($"No builds found for {Repo} this rollout; rollout marked as FAILED.", Log, LogLevel); return(true); } ScorecardBuildBreakdown lastBuild = BuildBreakdowns.Last(); Utilities.WriteDebug($"Last build is for {Repo} is {lastBuild.BuildSummary.BuildNumber} ({lastBuild.BuildSummary.WebLink})", Log, LogLevel); if (lastBuild.Score.Rollbacks == 1) { Utilities.WriteDebug($"Last build ({lastBuild.BuildSummary.BuildNumber}) was a rollback; rollout marked as FAILED.", Log, LogLevel); return(true); } string lastBuildResult = lastBuild.BuildSummary.Result; Utilities.WriteDebug($"Build {lastBuild.BuildSummary.BuildNumber} has result '{lastBuildResult}'", Log, LogLevel); switch (lastBuildResult) { case "succeeded": case "partiallySucceeded": Utilities.WriteDebug($"Last build determined successful.", Log, LogLevel); return(false); default: Utilities.WriteDebug($"Last build determined unsuccessful; rollout marked as FAILED.", Log, LogLevel); return(true); } }
public async Task InitAsync() { // Convert the rollout start time and end time to the strings the AzDO API recognizes and fetch builds string rolloutStartTimeUriString = RolloutStartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); string rolloutEndTimeUriString = RolloutEndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); foreach (string buildDefinitionId in RepoConfig.BuildDefinitionIds) { string azdoQuery = $"https://dev.azure.com/{RepoConfig.AzdoInstance}/" + $"{AzdoConfig.Project}/_apis/build/builds?definitions={buildDefinitionId}&branchName={Branch}" + $"&minTime={rolloutStartTimeUriString}&maxTime={rolloutEndTimeUriString}&api-version=5.1"; Utilities.WriteDebug($"Querying AzDO API: {azdoQuery}", Log, LogLevel); JObject responseContent = await GetAzdoApiResponseAsync(azdoQuery); // No builds is a valid case (e.g. a failed rollout) and so the rest of the code can handle this // It still is potentially unexpected, so we're going to warn the user here if (responseContent.Value <int>("count") == 0) { Utilities.WriteWarning($"No builds were found for repo '{RepoConfig.Repo}' " + $"(Build ID: '{buildDefinitionId}') during the specified dates ({RolloutStartDate} to {RolloutEndDate})", Log); } JArray builds = responseContent.Value <JArray>("value"); foreach (JToken build in builds) { BuildBreakdowns.Add(new ScorecardBuildBreakdown(build.ToObject <BuildSummary>())); } } BuildBreakdowns = BuildBreakdowns.OrderBy(x => x.BuildSummary.FinishTime).ToList(); Utilities.WriteDebug($"Builds breakdowns created for: \n\t {string.Join(" \n\t ", BuildBreakdowns.Select(b => b?.BuildSummary?.WebLink ?? ""))}", Log, LogLevel); await Task.WhenAll(BuildBreakdowns.Select(b => CollectStages(b))); }
/// <summary> /// Calculates and returns the number of hotfixes and rollouts that occurred as part of the build /// </summary> /// <returns>The number of hotfixes and rollbacks which occurred as part of the rollout</returns> public async Task <(int numHotfixes, int numRollbacks)> CalculateNumHotfixesAndRollbacksFromAzdoAsync() { // Any attempt to figure out whether a deployment succeeded or failed will be inherently flawed // The process used here is as follows: // * Find the first build where a deployment stage was reached; assume this means some sort of deployment happened // * Every build after that also makes it to deployment (and is tagged "HOTFIX" when --assume-no-tags is not set) is a hotfix int numHotfixes = 0; int numRollbacks = 0; // This is a list of all builds that were part of this rollout which reached a deployment stage // We skip the first deployment because only things after that one can be hotfixes or rollbacks IEnumerable <ScorecardBuildBreakdown> buildsToCheck = BuildBreakdowns.Where(b => b.BuildSummary.DeploymentReached).Skip(1); foreach (ScorecardBuildBreakdown build in buildsToCheck) { BuildSource source = (await GetAzdoApiResponseAsync(build.BuildSummary.SourceLink)).ToObject <BuildSource>(); // we can only automatically calculate rollbacks if they're tagged; so we specifically don't try when --assume-no-tags is passed if (!AssumeNoTags && source.Comment.Contains(AzureDevOpsCommitTags.RollbackTag, StringComparison.InvariantCultureIgnoreCase)) { numRollbacks++; build.Score.Rollbacks = 1; Utilities.WriteDebug($"Build {build.BuildSummary.BuildNumber} determined to be a ROLLBACK with commit message '{source.Comment}'", Log, LogLevel); Utilities.WriteDebug($"Web link: {build.BuildSummary.WebLink}", Log, LogLevel); } // if we're assuming no tags, every deployment after the first is assumed to be a hotfix; otherwise we need to look specifically for the hotfix tag else if (AssumeNoTags || source.Comment.Contains(AzureDevOpsCommitTags.HotfixTag, StringComparison.InvariantCultureIgnoreCase)) { numHotfixes++; build.Score.Hotfixes = 1; Utilities.WriteDebug($"Build {build.BuildSummary.BuildNumber} determined to be a HOTFIX with commit message '{source.Comment}'", Log, LogLevel); Utilities.WriteDebug($"Web link: {build.BuildSummary.WebLink}", Log, LogLevel); } // if none of these caught this deployment, then there's an untagged deployment when tags should be present; we'll warn the user about this else if (!source.Comment.Contains(AzureDevOpsCommitTags.RolloutTag, StringComparison.InvariantCultureIgnoreCase)) { Utilities.WriteWarning($"Untagged deployment found: build number '{build.BuildSummary.BuildNumber}' with commit message '{source.Comment}'", Log); Utilities.WriteWarning($"Web link: {build.BuildSummary.WebLink}", Log); } } numHotfixes += ManualHotfixes; numRollbacks += ManualRollbacks; Utilities.WriteDebug($"Detected {numHotfixes} hotfixes ({ManualHotfixes} manual) and {numRollbacks} rollbacks ({ManualRollbacks} manual).", Log, LogLevel); return(numHotfixes, numRollbacks); }
public bool DetermineFailure() { if (BuildBreakdowns.Count == 0) { return(true); } string lastBuildResult = BuildBreakdowns.Last().BuildSummary.Result; switch (lastBuildResult) { case "succeeded": case "partiallySucceeded": return(false); default: return(true); } }
public bool DetermineFailure(List <Issue> githubIssues) { Utilities.WriteDebug($"Determining failure for {Repo}...", Log, LogLevel); if (githubIssues.Any(i => Utilities.IssueContainsRelevantLabels(i, GithubLabelNames.FailureLabel, RepoConfig.GithubIssueLabel, Log, LogLevel))) { Utilities.WriteDebug($"Issue with failure tag found for {Repo}; rollout marked as FAILED", Log, LogLevel); return(true); } if (BuildBreakdowns.Count == 0) { Utilities.WriteDebug($"No builds found for {Repo} this rollout; rollout marked as FAILED.", Log, LogLevel); return(true); } ScorecardBuildBreakdown lastBuild = BuildBreakdowns.Last(); Utilities.WriteDebug($"Last build is for {Repo} is {lastBuild.BuildSummary.BuildNumber} ({lastBuild.BuildSummary.WebLink})", Log, LogLevel); if (lastBuild.Score.Rollbacks == 1) { Utilities.WriteDebug($"Last build ({lastBuild.BuildSummary.BuildNumber}) was a rollback; rollout marked as FAILED.", Log, LogLevel); return(true); } string lastBuildResult = lastBuild.BuildSummary.Result; Utilities.WriteDebug($"Build {lastBuild.BuildSummary.BuildNumber} has result '{lastBuildResult}'", Log, LogLevel); switch (lastBuildResult) { case "succeeded": case "partiallySucceeded": Utilities.WriteDebug($"Last build determined successful.", Log, LogLevel); return(false); default: Utilities.WriteDebug($"Last build determined unsuccessful; rollout marked as FAILED.", Log, LogLevel); return(true); } }
public async Task InitAsync() { // Convert the rollout start time and end time to the strings the AzDO API recognizes and fetch builds string rolloutStartTimeUriString = RolloutStartDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); string rolloutEndTimeUriString = RolloutEndDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); JObject responseContent = await GetAzdoApiResponseAsync($"https://dev.azure.com/{RepoConfig.AzdoInstance}/" + $"{AzdoConfig.Project}/_apis/build/builds?definitions={RepoConfig.DefinitionId}&branchName={Branch}" + $"&minTime={rolloutStartTimeUriString}&maxTime={rolloutEndTimeUriString}&api-version=5.1"); // No builds is a valid case (e.g. a failed rollout) and so the rest of the code can handle this // It still is potentially unexpected, so we're going to warn the user here if (responseContent.Value <int>("count") == 0) { Utilities.WriteWarning($"No builds were found for repo '{RepoConfig.Repo}' " + $"(Build ID: '{RepoConfig.DefinitionId}') during the specified dates ({RolloutStartDate} to {RolloutEndDate})", Log); } JArray builds = responseContent.Value <JArray>("value"); foreach (JToken build in builds) { BuildBreakdowns.Add(new ScorecardBuildBreakdown(build.ToObject <BuildSummary>())); } }
public bool DetermineFailure() { return(BuildBreakdowns.Count == 0 || BuildBreakdowns.Last().BuildSummary.Result != "succeeded"); }