Skip to content

Commit

Permalink
Allow forcing releases from a specific commit sha (#2509) (#2510)
Browse files Browse the repository at this point in the history
This should not be the default approach, but it allows us to create releases without waiting for a full rebuild to occur, or where we have issues with temporary flake/capacity in CI.

This does some minimal verification of the provided sha (that it's on the branch provided) and checks that the artifacts are available, but does not require the build to have succeeded
  • Loading branch information
andrewlock committed Feb 25, 2022
1 parent 88b1947 commit 57bc388
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 40 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/create_draft_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ name: Create draft release

on:
workflow_dispatch:
inputs:
forced_commit_id:
description: 'Force using artifacts from specific commit? If provided, this will try and use the artifacts from the given commit, regardless of build status'
required: false

jobs:
create_draft_release:
Expand Down Expand Up @@ -34,6 +38,7 @@ jobs:
run: ./tracer/build.sh DownloadAzurePipelineAndGitlabArtifacts
env:
TargetBranch: ${{ github.event.ref }}
CommitSha: "${{ github.event.inputs.forced_commit_id }}"

- name: "Generate release notes"
id: release_notes
Expand Down
135 changes: 95 additions & 40 deletions tracer/build/_build/Build.GitHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ partial class Build
[Parameter("The Pull Request number for GitHub Actions")]
readonly int? PullRequestNumber;

[Parameter("The specific commit sha to use", List = false)]
readonly string CommitSha;

[Parameter("The git branch to use", List = false)]
readonly string TargetBranch;

Expand Down Expand Up @@ -490,57 +493,109 @@ await client.Issue.Milestone.Update(
BuildArtifact artifact = null;
var artifactName = $"{FullVersion}-release-artifacts";
string commitSha = CommitSha;
Logger.Info($"Checking builds for artifact called: {artifactName}");
string commitSha = String.Empty;
// start from the current commit, and keep looking backwards until we find a commit that has a build
// that has successful artifacts. Should only be called from branches with a linear history (i.e. single parent)
// This solves a potential issue where we previously selecting a build by start order, not by the actual
// git commit order. Generally that shouldn't be an issue, but if we manually trigger builds on master
// (which we sometimes do e.g. trying to bisect and issue, or retrying flaky test for coverage reasons),
// then we could end up selecting the wrong build.
const int maxCommitsBack = 20;
for (var i = 0; i < maxCommitsBack; i++)
if (!string.IsNullOrEmpty(CommitSha))
{
commitSha = GitTasks.Git($"log {TargetBranch}~{i} -1 --pretty=%H")
.FirstOrDefault(x => x.Type == OutputType.Std)
.Text;
var foundSha = false;
var maxCommitsBack = 20;
// basic verification, to ensure that the provided commitsha is actually on this branch
for (var i = 0; i < maxCommitsBack; i++)
{
var sha = GitTasks.Git($"log {TargetBranch}~{i} -1 --pretty=%H")
.FirstOrDefault(x => x.Type == OutputType.Std)
.Text;
Logger.Info($"Looking for builds for {commitSha}");
if (string.Equals(CommitSha, sha, StringComparison.OrdinalIgnoreCase))
{
// OK, this SHA is definitely on this branch
foundSha = true;
break;
}
}
foreach (var build in builds)
if (!foundSha)
{
if (string.Equals(build.SourceVersion, commitSha, StringComparison.OrdinalIgnoreCase))
{
// Found a build for the commit, so should be successful and have an artifact
if (build.Result != BuildResult.Succeeded && build.Result != BuildResult.PartiallySucceeded)
{
Logger.Error($"::error::The build for commit {commitSha} was not successful. Please retry any failed stages for the build before creating a release");
throw new Exception("Latest build for branch was not successful. Please retry the build before creating a release");
}
Logger.Error($"Error: The commit {CommitSha} could not be found in the last {maxCommitsBack} of the branch {TargetBranch}" +
$"Ensure that the commit sha you have provided is correct, and you are running the create_release action from the correct branch");
throw new Exception($"The commit {CommitSha} could not found in the latest {maxCommitsBack} of target branch {TargetBranch}");
}
try
{
artifact = await buildHttpClient.GetArtifactAsync(
project: AzureDevopsProjectId,
buildId: build.Id,
artifactName: artifactName);
break;
}
catch (ArtifactNotFoundException)
Logger.Info($"Finding build for commit sha: {CommitSha}");
var build = builds
.FirstOrDefault(b => string.Equals(b.SourceVersion, CommitSha, StringComparison.OrdinalIgnoreCase));
if (build is null)
{
throw new Exception($"No builds for commit {CommitSha} found. Please check you have provided the correct SHA, and that there is a build in AzureDevops for the commit");
}
try
{
artifact = await buildHttpClient.GetArtifactAsync(
project: AzureDevopsProjectId,
buildId: build.Id,
artifactName: artifactName);
}
catch (ArtifactNotFoundException)
{
Logger.Error($"Error: The build {build.Id} for commit could not find {artifactName} artifact for build {build.Id} for commit {commitSha}. " +
$"Ensure the build has successfully generated artifacts for this commit before creating a release");
throw;
}
}
else
{
Logger.Info($"Checking builds for artifact called: {artifactName}");
// start from the current commit, and keep looking backwards until we find a commit that has a build
// that has successful artifacts. Should only be called from branches with a linear history (i.e. single parent)
// This solves a potential issue where we previously selecting a build by start order, not by the actual
// git commit order. Generally that shouldn't be an issue, but if we manually trigger builds on master
// (which we sometimes do e.g. trying to bisect and issue, or retrying flaky test for coverage reasons),
// then we could end up selecting the wrong build.
const int maxCommitsBack = 20;
for (var i = 0; i < maxCommitsBack; i++)
{
commitSha = GitTasks.Git($"log {TargetBranch}~{i} -1 --pretty=%H")
.FirstOrDefault(x => x.Type == OutputType.Std)
.Text;
Logger.Info($"Looking for builds for {commitSha}");
foreach (var build in builds)
{
if (string.Equals(build.SourceVersion, commitSha, StringComparison.OrdinalIgnoreCase))
{
Logger.Error($"Error: could not find {artifactName} artifact for build {build.Id} for commit {commitSha}. " +
$"Ensure the build has completed successfully for this commit before creating a release");
throw;
// Found a build for the commit, so should be successful and have an artifact
if (build.Result != BuildResult.Succeeded && build.Result != BuildResult.PartiallySucceeded)
{
Logger.Error($"::error::The build for commit {commitSha} was not successful. Please retry any failed stages for the build before creating a release");
throw new Exception("Latest build for branch was not successful. Please retry the build before creating a release");
}
try
{
artifact = await buildHttpClient.GetArtifactAsync(
project: AzureDevopsProjectId,
buildId: build.Id,
artifactName: artifactName);
break;
}
catch (ArtifactNotFoundException)
{
Logger.Error($"Error: could not find {artifactName} artifact for build {build.Id} for commit {commitSha}. " +
$"Ensure the build has completed successfully for this commit before creating a release");
throw;
}
}
}
}
if (artifact is not null)
{
break;
if (artifact is not null)
{
break;
}
}
}
Expand Down

0 comments on commit 57bc388

Please sign in to comment.