/// <summary>
 /// Construct <see cref="BuildStepResult"/> from this <see cref="BuildStep"/> that represent a failed execution.
 /// </summary>
 /// <param name="message">Message that explain why the <see cref="BuildStep"/> execution failed.</param>
 /// <returns>A new <see cref="BuildStepResult"/> instance.</returns>
 public BuildStepResult Failure(string message) => BuildStepResult.Failure(this, message);
        BuildPipelineResult RunBuildSteps(BuildContext context)
        {
            var timer  = new Stopwatch();
            var status = context.BuildPipelineStatus;
            var title  = context.BuildProgress?.Title ?? string.Empty;

            // Setup build steps list
            var cleanupSteps = new Stack <BuildStep>();
            var enabledSteps = EnumerateBuildSteps().Where(step => step.IsEnabled(context)).ToArray();

            // Run build steps and stop on first failure of any kind
            for (var i = 0; i < enabledSteps.Length; ++i)
            {
                var step = enabledSteps[i];

                // Update build progress
                var cancelled = context.BuildProgress?.Update($"{title} (Step {i + 1} of {enabledSteps.Length})", step.Description + "...", (float)i / enabledSteps.Length) ?? false;
                if (cancelled)
                {
                    status.Succeeded = false;
                    status.Message   = $"{title} was cancelled.";
                    break;
                }

                // Add step to cleanup stack only if it overrides implementation
                if (step.GetType().GetMethod(nameof(BuildStep.CleanupBuildStep)).DeclaringType != typeof(BuildStep))
                {
                    cleanupSteps.Push(step);
                }

                // Run build step
                try
                {
                    timer.Restart();
                    var result = step.RunBuildStep(context);
                    timer.Stop();

                    // Update build step duration
                    result.Duration = timer.Elapsed;

                    // Add build step result to pipeline status
                    status.BuildStepsResults.Add(result);

                    // Stop execution for normal build steps after failure
                    if (!result.Succeeded)
                    {
                        break;
                    }
                }
                catch (Exception exception)
                {
                    // Add build step exception to pipeline status, and stop executing build steps
                    status.BuildStepsResults.Add(BuildStepResult.Failure(step, exception));
                    break;
                }
            }

            // Execute cleanup even if there are failures in build steps.
            // * In opposite order of build steps that ran
            // * Can't be cancelled; cleanup step must run
            foreach (var step in cleanupSteps)
            {
                // Update build progress
                context.BuildProgress?.Update($"{title} (Cleanup)", step.Description + "...", 1.0F);

                // Run cleanup step
                try
                {
                    timer.Restart();
                    var result = step.CleanupBuildStep(context);
                    timer.Stop();

                    // Update cleanup step duration
                    result.Duration = timer.Elapsed;

                    // Add cleanup step result to pipeline status
                    status.BuildStepsResults.Add(result);
                }
                catch (Exception exception)
                {
                    // Add cleanup step exception to pipeline status (not stopping execution)
                    status.BuildStepsResults.Add(BuildStepResult.Failure(step, exception));
                }
            }

            return(status);
        }