Skip to content

Commit

Permalink
WIP: Implementation of Jenkins (controller) executor.
Browse files Browse the repository at this point in the history
  • Loading branch information
JasperDeLaat94 authored and Jasper de Laat committed Jan 14, 2025
1 parent 7f28719 commit 7f246c5
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ protected override async Task EmitBuildServerSpecificFileAsync(BuildSpecificatio
{
buildCommandString += $"$env:{kv.Key}=\'{kv.Value}\'\n";
}
buildCommandString += $"$env:UET_GIT_URL=\'{_gitUri!.ToString}\'\n";
buildCommandString += $"$env:UET_GIT_URL=\'{_gitUri}\'\n";
buildCommandString += $"$env:UET_GIT_REF=\'{_gitBranch}\'\n";

buildCommandString += jobData.Script("jenkins");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
{
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Redpoint.Uet.BuildPipeline.Executors.BuildServer;
using Redpoint.Uet.BuildPipeline.Executors.Engine;
using Redpoint.Uet.Workspace;

public class JenkinsBuildExecutorFactory
{
Expand All @@ -22,5 +25,14 @@ public IBuildExecutor CreateExecutor(string buildServerOutputFilePath, Uri? gitU
gitUrl,
gitBranch);
}

public IBuildNodeExecutor CreateNodeExecutor()
{
return new JenkinsBuildNodeExecutor(
_serviceProvider,
_serviceProvider.GetRequiredService<ILogger<JenkinsBuildNodeExecutor>>(),
_serviceProvider.GetRequiredService<IEngineWorkspaceProvider>(),
_serviceProvider.GetRequiredService<IDynamicWorkspaceProvider>());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,165 @@
namespace Redpoint.Uet.BuildPipeline.Executors.Jenkins
{
using Microsoft.Extensions.Logging;
using Redpoint.Concurrency;
using Redpoint.Uet.BuildPipeline.Executors;
using Redpoint.Uet.BuildPipeline.Executors.BuildServer;
using Redpoint.Uet.BuildPipeline.Executors.Engine;
using Redpoint.Uet.Configuration.Dynamic;
using Redpoint.Uet.Configuration.Plugin;
using Redpoint.Uet.Configuration.Project;
using Redpoint.Uet.Workspace;
using Redpoint.Uet.Workspace.Descriptors;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class JenkinsBuildNodeExecutor : IBuildNodeExecutor
{
private readonly ILogger<JenkinsBuildNodeExecutor> _logger;
private readonly IEngineWorkspaceProvider _engineWorkspaceProvider;
private readonly IDynamicWorkspaceProvider _workspaceProvider;

public JenkinsBuildNodeExecutor(
IServiceProvider serviceProvider,
ILogger<JenkinsBuildNodeExecutor> logger,
IEngineWorkspaceProvider engineWorkspaceProvider,
IDynamicWorkspaceProvider workspaceProvider)
{
_logger = logger;
_engineWorkspaceProvider = engineWorkspaceProvider;
_workspaceProvider = workspaceProvider;
}

public string DiscoverPipelineId()
{
// TODO: This might be wrong if the ID is expected to be the same between the main executor and node executors, investigate.
return Environment.GetEnvironmentVariable("BUILD_TAG") ?? string.Empty;
}

public Task<int> ExecuteBuildNodesAsync(BuildSpecification buildSpecification, BuildConfigDynamic<BuildConfigPluginDistribution, IPrepareProvider>[]? preparePlugin, BuildConfigDynamic<BuildConfigProjectDistribution, IPrepareProvider>[]? prepareProject, IBuildExecutionEvents buildExecutionEvents, IReadOnlyList<string> nodeNames, CancellationToken cancellationToken)
private class NodeNameExecutionState
{
public string? NodeName;
}

public async Task<int> ExecuteBuildNodesAsync(
BuildSpecification buildSpecification,
BuildConfigDynamic<BuildConfigPluginDistribution, IPrepareProvider>[]? preparePlugin,
BuildConfigDynamic<BuildConfigProjectDistribution, IPrepareProvider>[]? prepareProject,
IBuildExecutionEvents buildExecutionEvents,
IReadOnlyList<string> nodeNames,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
ArgumentNullException.ThrowIfNull(buildSpecification);
ArgumentNullException.ThrowIfNull(buildExecutionEvents);
ArgumentNullException.ThrowIfNull(nodeNames);

var repository = Environment.GetEnvironmentVariable("UET_GIT_URL")!;
var commit = Environment.GetEnvironmentVariable("UET_GIT_REF")!;
var executingNode = new NodeNameExecutionState();

_logger.LogInformation($"Repository: {repository}, branch: {commit}");

_logger.LogTrace("Starting execution of nodes...");
try
{
await using ((await _engineWorkspaceProvider.GetEngineWorkspace(
buildSpecification.Engine,
string.Empty,
cancellationToken).ConfigureAwait(false)).AsAsyncDisposable(out var engineWorkspace).ConfigureAwait(false))
{
async Task<int> ExecuteNodeInWorkspaceAsync(string nodeName, string targetWorkspacePath)
{
await Task.Delay(1, cancellationToken).ConfigureAwait(false);
throw new NotImplementedException();
}

async Task<int> ExecuteNodesInWorkspaceAsync(string targetWorkspacePath)
{
foreach (var nodeName in nodeNames)
{
await buildExecutionEvents.OnNodeStarted(nodeName).ConfigureAwait(false);
executingNode.NodeName = nodeName;
var exitCode = await ExecuteNodeInWorkspaceAsync(nodeName, targetWorkspacePath).ConfigureAwait(false);
if (exitCode == 0)
{
_logger.LogTrace($"Finished: {nodeName} = Success");
executingNode.NodeName = null;
await buildExecutionEvents.OnNodeFinished(nodeName, BuildResultStatus.Success).ConfigureAwait(false);
continue;
}
else
{
_logger.LogTrace($"Finished: {nodeName} = Failed");
executingNode.NodeName = null;
await buildExecutionEvents.OnNodeFinished(nodeName, BuildResultStatus.Failed).ConfigureAwait(false);
return 1;
}
}
return 0;
}

int overallExitCode;
if (buildSpecification.Engine.IsEngineBuild)
{
overallExitCode = await ExecuteNodesInWorkspaceAsync(engineWorkspace.Path).ConfigureAwait(false);
}
else
{
_logger.LogTrace($"Obtaining workspace for build.");
await using ((await _workspaceProvider.GetWorkspaceAsync(
new GitWorkspaceDescriptor
{
RepositoryUrl = repository,
RepositoryCommitOrRef = commit,
AdditionalFolderLayers = Array.Empty<string>(),
AdditionalFolderZips = Array.Empty<string>(),
WorkspaceDisambiguators = nodeNames,
ProjectFolderName = buildSpecification.ProjectFolderName,
BuildType = GitWorkspaceDescriptorBuildType.Generic,
WindowsSharedGitCachePath = null,
MacSharedGitCachePath = null,
},
cancellationToken).ConfigureAwait(false)).AsAsyncDisposable(out var targetWorkspace).ConfigureAwait(false))
{
_logger.LogTrace($"Calling ExecuteNodesInWorkspaceAsync inside allocated workspace.");
overallExitCode = await ExecuteNodesInWorkspaceAsync(targetWorkspace.Path).ConfigureAwait(false);
_logger.LogTrace($"Finished ExecuteNodesInWorkspaceAsync with exit code '{overallExitCode}'.");
}
_logger.LogTrace($"Released workspace for build.");
}
_logger.LogTrace($"Returning overall exit code '{overallExitCode}' from ExecuteBuildNodesAsync.");
return overallExitCode;
}
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
// The build was cancelled.
_logger.LogTrace("Detected build cancellation.");
var currentNodeName = executingNode.NodeName;
if (currentNodeName != null)
{
_logger.LogTrace($"Finished: {currentNodeName} = Cancelled");
await buildExecutionEvents.OnNodeFinished(currentNodeName, BuildResultStatus.Cancelled).ConfigureAwait(false);
}
return 1;
}
catch (Exception ex)
{
_logger.LogTrace($"Detected build failure due to exception: {ex}");
var currentNodeName = executingNode.NodeName;
if (currentNodeName != null)
{
_logger.LogError(ex, $"Internal exception while running build job {currentNodeName}: {ex.Message}");
await buildExecutionEvents.OnNodeFinished(currentNodeName, BuildResultStatus.Failed).ConfigureAwait(false);
}
else
{
_logger.LogError(ex, $"Internal exception prior to running named build job: {ex.Message}");
}
return 1;
}
}
}
}
20 changes: 11 additions & 9 deletions UET/uet/Commands/Internal/CIBuild/CIBuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using Redpoint.Uet.BuildPipeline.Executors;
using Redpoint.Uet.BuildPipeline.Executors.BuildServer;
using Redpoint.Uet.BuildPipeline.Executors.GitLab;
using Redpoint.Uet.BuildPipeline.Executors.Jenkins;
using Redpoint.Uet.BuildPipeline.Executors.Local;
using Redpoint.Uet.Core;
using Redpoint.Uet.Core.Permissions;
using Redpoint.Uet.Workspace;
Expand All @@ -32,7 +34,7 @@ public Options()
description: "The executor to use.",
getDefaultValue: () => "gitlab");
Executor.AddAlias("-x");
Executor.FromAmong("gitlab");
Executor.FromAmong("gitlab", "jenkins");
}
}

Expand All @@ -50,6 +52,7 @@ private sealed class CIBuildCommandInstance : ICommandInstance
private readonly ILogger<CIBuildCommandInstance> _logger;
private readonly Options _options;
private readonly GitLabBuildExecutorFactory _gitLabBuildExecutorFactory;
private readonly JenkinsBuildExecutorFactory _jenkinsBuildExecutorFactory;
private readonly IWorldPermissionApplier _worldPermissionApplier;
private readonly IDynamicWorkspaceProvider _dynamicWorkspaceProvider;
private readonly IStorageManagement _storageManagement;
Expand All @@ -59,6 +62,7 @@ public CIBuildCommandInstance(
ILogger<CIBuildCommandInstance> logger,
Options options,
GitLabBuildExecutorFactory gitLabBuildExecutorFactory,
JenkinsBuildExecutorFactory jenkinsBuildExecutorFactory,
IWorldPermissionApplier worldPermissionApplier,
IServiceProvider serviceProvider,
IDynamicWorkspaceProvider dynamicWorkspaceProvider,
Expand All @@ -67,6 +71,7 @@ public CIBuildCommandInstance(
_logger = logger;
_options = options;
_gitLabBuildExecutorFactory = gitLabBuildExecutorFactory;
_jenkinsBuildExecutorFactory = jenkinsBuildExecutorFactory;
_worldPermissionApplier = worldPermissionApplier;
_dynamicWorkspaceProvider = dynamicWorkspaceProvider;
_storageManagement = storageManagement;
Expand Down Expand Up @@ -130,15 +135,12 @@ await _storageManagement.AutoPurgeStorageAsync(
engine.WindowsSharedGitCachePath,
engine.MacSharedGitCachePath);

IBuildNodeExecutor executor;
switch (executorName)
var executor = executorName switch
{
case "gitlab":
executor = _gitLabBuildExecutorFactory.CreateNodeExecutor();
break;
default:
throw new NotSupportedException();
}
"gitlab" => _gitLabBuildExecutorFactory.CreateNodeExecutor(),
"jenkins" => _jenkinsBuildExecutorFactory.CreateNodeExecutor(),
_ => throw new NotSupportedException(),
};

BuildGraphEnvironment environment;
if (OperatingSystem.IsWindows())
Expand Down

0 comments on commit 7f246c5

Please sign in to comment.