// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Json.Serialization;
using EpicGames.Horde.Artifacts;
using EpicGames.Horde.Commits;
using EpicGames.Horde.Common;
using EpicGames.Horde.Jobs.Bisect;
using EpicGames.Horde.Jobs.Templates;
using EpicGames.Horde.Streams;
using EpicGames.Horde.Users;
#pragma warning disable CA1056 // Change the type of property 'JobContainerOptions.ImageUrl' from 'string' to 'System.Uri'
#pragma warning disable CA2227 // Change 'Outcomes' to be read-only by removing the property setter
namespace EpicGames.Horde.Jobs
{
///
/// State of the job
///
public enum JobState
{
///
/// Waiting for resources
///
Waiting,
///
/// Currently running one or more steps
///
Running,
///
/// All steps have completed
///
Complete,
}
///
/// Read-only interface for job options
///
public interface IReadOnlyJobOptions
{
///
/// Name of the executor to use
///
string? Executor { get; }
///
/// Whether to execute using Wine emulation on Linux
///
bool? UseWine { get; }
///
/// Executes the job lease in a separate process
///
bool? RunInSeparateProcess { get; }
///
/// What workspace materializer to use in WorkspaceExecutor. Will override any value from workspace config.
///
string? WorkspaceMaterializer { get; }
///
/// Options for executing a job inside a container
///
IReadOnlyJobContainerOptions Container { get; }
///
/// Number of days after which to expire jobs
///
int? ExpireAfterDays { get; }
///
/// Name of the driver to use
///
string? Driver { get; }
}
///
/// Options for executing a job
///
public class JobOptions : IReadOnlyJobOptions
{
///
public string? Executor { get; set; }
///
public bool? UseWine { get; set; }
///
public bool? RunInSeparateProcess { get; set; }
///
public string? WorkspaceMaterializer { get; set; }
///
public JobContainerOptions Container { get; set; } = new JobContainerOptions();
///
public int? ExpireAfterDays { get; set; }
///
public string? Driver { get; set; }
IReadOnlyJobContainerOptions IReadOnlyJobOptions.Container => Container;
///
/// Merge defaults from another options object
///
public void MergeDefaults(JobOptions other)
{
Executor ??= other.Executor;
UseWine ??= other.UseWine;
RunInSeparateProcess ??= other.RunInSeparateProcess;
WorkspaceMaterializer ??= other.WorkspaceMaterializer;
Container.MergeDefaults(other.Container);
ExpireAfterDays ??= other.ExpireAfterDays;
Driver ??= other.Driver;
}
}
///
/// Options for a job container
///
public interface IReadOnlyJobContainerOptions
{
///
/// Whether to execute job inside a container
///
public bool? Enabled { get; }
///
/// Image URL to container, such as "quay.io/podman/hello"
///
public string? ImageUrl { get; }
///
/// Container engine executable (docker or with full path like /usr/bin/podman)
///
public string? ContainerEngineExecutable { get; }
///
/// Additional arguments to pass to container engine
///
public string? ExtraArguments { get; }
}
///
/// Options for executing a job inside a container
///
public class JobContainerOptions : IReadOnlyJobContainerOptions
{
///
/// Whether to execute job inside a container
///
public bool? Enabled { get; set; }
///
/// Image URL to container, such as "quay.io/podman/hello"
///
public string? ImageUrl { get; set; }
///
/// Container engine executable (docker or with full path like /usr/bin/podman)
///
public string? ContainerEngineExecutable { get; set; }
///
/// Additional arguments to pass to container engine
///
public string? ExtraArguments { get; set; }
///
/// Merge defaults from another options object
///
public void MergeDefaults(JobContainerOptions other)
{
Enabled ??= other.Enabled;
ImageUrl ??= other.ImageUrl;
ContainerEngineExecutable ??= other.ContainerEngineExecutable;
ExtraArguments ??= other.ExtraArguments;
}
}
///
/// Query selecting the base changelist to use
///
public class ChangeQueryMessage : IChangeQuery
{
///
public string? Name { get; set; }
///
public Condition? Condition { get; set; }
///
public TemplateId? TemplateId { get; set; }
///
public string? Target { get; set; }
///
public List? Outcomes { get; set; }
IReadOnlyList? IChangeQuery.Outcomes => Outcomes;
///
public CommitTag? CommitTag { get; set; }
///
/// Constructor
///
public ChangeQueryMessage()
{ }
///
/// Constructor
///
public ChangeQueryMessage(IChangeQuery other)
{
Name = other.Name;
Condition = other.Condition;
TemplateId = other.TemplateId;
Target = other.Target;
Outcomes = other.Outcomes?.ToList();
CommitTag = other.CommitTag;
}
}
///
/// Parameters required to create a job
///
public class CreateJobRequest
{
///
/// The stream that this job belongs to
///
[Required]
public StreamId StreamId { get; set; }
///
/// The template for this job
///
[Required]
public TemplateId TemplateId { get; set; }
///
/// Name of the job
///
public string? Name { get; set; }
///
/// The changelist number to build. Can be null for latest.
///
[Obsolete("Use CommitId instead")]
public int? Change
{
get => _change ?? _commitId?.GetPerforceChangeOrMinusOne();
set => _change = value;
}
int? _change;
///
/// The changelist number to build. Can be null for latest.
///
public CommitId? CommitId
{
get => _commitId ?? CommitId.FromPerforceChange(_change);
set => _commitId = value;
}
CommitId? _commitId;
///
/// Parameters to use when selecting the change to execute at.
///
[Obsolete("Use ChangeQueries instead")]
public ChangeQueryMessage? ChangeQuery
{
get => (ChangeQueries != null && ChangeQueries.Count > 0) ? ChangeQueries[0] : null;
set => ChangeQueries = (value == null) ? null : new List { value };
}
///
/// List of change queries to evaluate
///
public List? ChangeQueries { get; set; }
///
/// The preflight changelist number
///
[Obsolete("Use PreflightCommitId instead")]
public int? PreflightChange
{
get => _preflightChange ?? _preflightCommitId?.GetPerforceChangeOrMinusOne();
set => _preflightChange = value;
}
int? _preflightChange;
///
/// The preflight commit
///
public CommitId? PreflightCommitId
{
get => _preflightCommitId ?? CommitId.FromPerforceChange(_preflightChange);
set => _preflightCommitId = value;
}
CommitId? _preflightCommitId;
///
/// Job options
///
public JobOptions? JobOptions { get; set; }
///
/// Priority for the job
///
public Priority? Priority { get; set; }
///
/// Whether to automatically submit the preflighted change on completion
///
public bool? AutoSubmit { get; set; }
///
/// Whether to update issues based on the outcome of this job
///
public bool? UpdateIssues { get; set; }
///
/// Values for the template parameters
///
public Dictionary? Parameters { get; set; }
///
/// Arguments for the job
///
public List? Arguments { get; set; }
///
/// Additional arguments for the job
///
public List? AdditionalArguments { get; set; }
///
/// Targets for the job. Will override any parameters specified in the Arguments or Parameters section if specified.
///
public List? Targets { get; set; }
///
/// The parent job id if any
///
public string? ParentJobId { get; set; }
///
/// The parent step id if any
///
public string? ParentJobStepId { get; set; }
///
/// Run the job as the scheduler for debugging purposes, requires debugging ACL permission
///
public bool? RunAsScheduler { get; set; }
///
/// Private constructor for serialization
///
public CreateJobRequest(StreamId streamId, TemplateId templateId)
{
StreamId = streamId;
TemplateId = templateId;
}
}
///
/// Response from creating a new job
///
public class CreateJobResponse
{
///
/// Unique id for the new job
///
public string Id { get; set; }
///
/// Constructor
///
/// Unique id for the new job
public CreateJobResponse(string id)
{
Id = id;
}
}
///
/// Updates an existing job
///
public class UpdateJobRequest
{
///
/// New name for the job
///
public string? Name { get; set; }
///
/// New priority for the job
///
public Priority? Priority { get; set; }
///
/// Set whether the job should be automatically submitted or not
///
public bool? AutoSubmit { get; set; }
///
/// Mark this job as aborted
///
public bool? Aborted { get; set; }
///
/// Optional reason the job was canceled
///
public string? CancellationReason { get; set; }
///
/// New list of arguments for the job. Only -Target= arguments can be modified after the job has started.
///
public List? Arguments { get; set; }
}
///
/// Job metadata put request
///
public class PutJobMetadataRequest
{
///
/// Meta data to append to the job
///
public List? JobMetaData { get; set; }
///
/// Step meta data to append, JobStepId => list of meta data strings
///
public Dictionary>? StepMetaData { get; set; }
}
///
/// Placement for a job report
///
public enum JobReportPlacement
{
///
/// On a panel of its own
///
Panel = 0,
///
/// In the summary panel
///
Summary = 1
}
///
/// Information about a report associated with a job
///
public class GetJobReportResponse
{
///
/// Name of the report
///
public string Name { get; set; }
///
/// Report placement
///
public JobReportPlacement Placement { get; set; }
///
/// The artifact id
///
public string? ArtifactId { get; set; }
///
/// Content for the report
///
public string? Content { get; set; }
///
/// Constructor
///
public GetJobReportResponse(string name, JobReportPlacement placement)
{
Name = name;
Placement = placement;
}
}
///
/// Information about a job
///
public class GetJobResponse
{
///
/// Unique Id for the job
///
public JobId Id { get; set; }
///
/// Name of the job
///
public string Name { get; set; }
///
/// Unique id of the stream containing this job
///
public StreamId StreamId { get; set; }
///
/// The changelist number to build
///
[Obsolete("Use CommitId instead")]
public int Change
{
get => _change ?? _commitId?.GetPerforceChangeOrMinusOne() ?? 0;
set => _change = value;
}
int? _change;
///
/// The commit to build
///
public CommitIdWithOrder CommitId
{
get => _commitId ?? CommitIdWithOrder.FromPerforceChange(_change) ?? CommitIdWithOrder.Empty;
set => _commitId = value;
}
CommitIdWithOrder? _commitId;
///
/// The code changelist
///
[Obsolete("Use CodeCommitId instead")]
public int? CodeChange
{
get => _codeChange ?? _codeCommitId?.GetPerforceChangeOrMinusOne();
set => _codeChange = value;
}
int? _codeChange;
///
/// The code commit to build
///
public CommitIdWithOrder? CodeCommitId
{
get => _codeCommitId ?? CommitIdWithOrder.FromPerforceChange(_codeChange);
set => _codeCommitId = value;
}
CommitIdWithOrder? _codeCommitId;
///
/// The preflight changelist number
///
[Obsolete("Use PreflightCommitId instead")]
public int? PreflightChange
{
get => _preflightChange ?? _preflightCommitId?.GetPerforceChangeOrMinusOne();
set => _preflightChange = value;
}
int? _preflightChange;
///
/// The preflight commit
///
public CommitId? PreflightCommitId
{
get => _preflightCommitId ?? Horde.Commits.CommitId.FromPerforceChange(_preflightChange);
set => _preflightCommitId = value;
}
CommitId? _preflightCommitId;
///
/// Description of the preflight
///
public string? PreflightDescription { get; set; }
///
/// The template type
///
public TemplateId TemplateId { get; set; }
///
/// Hash of the actual template data
///
public string? TemplateHash { get; set; }
///
/// Hash of the graph for this job
///
public string? GraphHash { get; set; }
///
/// The user that started this job [DEPRECATED]
///
public string? StartedByUserId { get; set; }
///
/// The user that started this job [DEPRECATED]
///
public string? StartedByUser { get; set; }
///
/// The user that started this job
///
public GetThinUserInfoResponse? StartedByUserInfo { get; set; }
///
/// Bisection task id that started this job
///
public BisectTaskId? StartedByBisectTaskId { get; set; }
///
/// The user that aborted this job [DEPRECATED]
///
public string? AbortedByUser { get; set; }
///
/// The user that aborted this job
///
public GetThinUserInfoResponse? AbortedByUserInfo { get; set; }
///
/// Optional reason the job was canceled
///
public string? CancellationReason { get; set; }
///
/// Priority of the job
///
public Priority Priority { get; set; }
///
/// Whether the change will automatically be submitted or not
///
public bool AutoSubmit { get; set; }
///
/// The submitted changelist number
///
public int? AutoSubmitChange { get; set; }
///
/// Message produced by trying to auto-submit the change
///
public string? AutoSubmitMessage { get; set; }
///
/// Time that the job was created
///
public DateTimeOffset CreateTime { get; set; }
///
/// The global job state
///
public JobState State { get; set; }
///
/// Array of jobstep batches
///
public List? Batches { get; set; }
///
/// List of labels
///
public List? Labels { get; set; }
///
/// The default label, containing the state of all steps that are otherwise not matched.
///
public GetDefaultLabelStateResponse? DefaultLabel { get; set; }
///
/// List of reports
///
public List? Reports { get; set; }
///
/// Artifacts produced by this job
///
public List? Artifacts { get; set; }
///
/// Parameters for the job
///
public Dictionary Parameters { get; set; } = new Dictionary();
///
/// Command line arguments for the job
///
public List Arguments { get; set; } = new List();
///
/// Additional command line arguments for the job for when using the parameters block
///
public List AdditionalArguments { get; set; } = new List();
///
/// Custom list of targets for the job
///
public List? Targets { get; set; }
///
/// List of metadata for this job
///
public List? Metadata { get; set; }
///
/// The last update time for this job
///
public DateTimeOffset UpdateTime { get; set; }
///
/// Whether to use the V2 artifacts endpoint
///
public bool UseArtifactsV2 { get; set; }
///
/// Whether issues are being updated by this job
///
public bool UpdateIssues { get; set; }
///
/// Whether the current user is allowed to update this job
///
public bool CanUpdate { get; set; } = true;
///
/// The parent job id, if any
///
public string? ParentJobId { get; set; }
///
/// The parent job step id, if any
///
public string? ParentJobStepId { get; set; }
///
/// Constructor
///
public GetJobResponse(JobId jobId, StreamId streamId, TemplateId templateId, string name)
{
Id = jobId;
StreamId = streamId;
TemplateId = templateId;
Name = name;
}
///
/// Default constructor needed for JsonSerializer
///
[JsonConstructor]
public GetJobResponse()
{
Name = "";
}
}
///
/// Response describing an artifact produced during a job
///
/// Identifier for this artifact, if it has been produced
/// Name of the artifact
/// Artifact type
/// Description to display for the artifact on the dashboard
/// Keys for the artifact
/// Metadata for the artifact
/// Step producing the artifact
public record class GetJobArtifactResponse
(
ArtifactId? Id,
ArtifactName Name,
ArtifactType Type,
string? Description,
List Keys,
List Metadata,
JobStepId StepId
);
///
/// The timing info for a job
///
public class GetJobTimingResponse
{
///
/// The job response
///
public GetJobResponse? JobResponse { get; set; }
///
/// Timing info for each step
///
public Dictionary Steps { get; set; }
///
/// Timing information for each label
///
public List Labels { get; set; }
///
/// Constructor
///
/// The job response
/// Timing info for each steps
/// Timing info for each label
public GetJobTimingResponse(GetJobResponse? jobResponse, Dictionary steps, List labels)
{
JobResponse = jobResponse;
Steps = steps;
Labels = labels;
}
}
///
/// The timing info for
///
public class FindJobTimingsResponse
{
///
/// Timing info for each job
///
public Dictionary Timings { get; set; }
///
/// Constructor
///
/// Timing info for each job
public FindJobTimingsResponse(Dictionary timings)
{
Timings = timings;
}
}
///
/// Request used to update a jobstep
///
public class UpdateStepRequest
{
///
/// The new jobstep state
///
public JobStepState State { get; set; } = JobStepState.Unspecified;
///
/// Outcome from the jobstep
///
public JobStepOutcome Outcome { get; set; } = JobStepOutcome.Unspecified;
///
/// If the step has been requested to abort
///
public bool? AbortRequested { get; set; }
///
/// Optional reason the job step was canceled
///
public string? CancellationReason { get; set; }
///
/// Specifies the log file id for this step
///
public string? LogId { get; set; }
///
/// Whether the step should be re-run
///
public bool? Retry { get; set; }
///
/// New priority for this step
///
public Priority? Priority { get; set; }
///
/// Properties to set. Any entries with a null value will be removed.
///
public Dictionary? Properties { get; set; }
}
///
/// Response object when updating a jobstep
///
public class UpdateStepResponse
{
///
/// If a new step is created (due to specifying the retry flag), specifies the batch id
///
public string? BatchId { get; set; }
///
/// If a step is retried, includes the new step id
///
public string? StepId { get; set; }
}
///
/// Reference to the output of a step within the job
///
/// Step producing the output
/// Index of the output from this step
public record struct JobStepOutputRef(JobStepId StepId, int OutputIdx);
///
/// Returns information about a jobstep
///
public class GetJobStepResponse
{
///
/// The unique id of the step
///
public JobStepId Id { get; set; }
///
/// Index of the node which this jobstep is to execute
///
public int NodeIdx { get; set; }
///
/// The name of this node
///
public string Name { get; set; } = String.Empty;
///
/// Whether this node can be run multiple times
///
public bool AllowRetry { get; set; }
///
/// This node can start running early, before dependencies of other nodes in the same group are complete
///
public bool RunEarly { get; set; }
///
/// Whether to include warnings in the output (defaults to true)
///
public bool Warnings { get; set; }
///
/// References to inputs for this node
///
public List? Inputs { get; set; }
///
/// List of output names
///
public List? OutputNames { get; set; }
///
/// Indices of nodes which must have succeeded for this node to run
///
public List? InputDependencies { get; set; }
///
/// Indices of nodes which must have completed for this node to run
///
public List? OrderDependencies { get; set; }
///
/// List of credentials required for this node. Each entry maps an environment variable name to a credential in the form "CredentialName.PropertyName".
///
public IReadOnlyDictionary? Credentials { get; set; }
///
/// Annotations for this node
///
public IReadOnlyDictionary? Annotations { get; set; }
///
/// List of metadata for this step
///
public List? Metadata { get; set; }
///
/// Current state of the job step. This is updated automatically when runs complete.
///
public JobStepState State { get; set; }
///
/// Current outcome of the jobstep
///
public JobStepOutcome Outcome { get; set; }
///
/// Error describing additional context for why a step failed to complete
///
public JobStepError Error { get; set; }
///
/// If the step has been requested to abort
///
public bool AbortRequested { get; set; }
///
/// Name of the user that requested the abort of this step [DEPRECATED]
///
public string? AbortByUser { get; set; }
///
/// The user that requested this step be run again
///
public GetThinUserInfoResponse? AbortedByUserInfo { get; set; }
///
/// Optional reason the job step was canceled
///
public string? CancellationReason { get; set; }
///
/// Name of the user that requested this step be run again [DEPRECATED]
///
public string? RetryByUser { get; set; }
///
/// The user that requested this step be run again
///
public GetThinUserInfoResponse? RetriedByUserInfo { get; set; }
///
/// The log id for this step
///
public string? LogId { get; set; }
///
/// Time at which the batch was ready (UTC).
///
public DateTimeOffset? ReadyTime { get; set; }
///
/// Time at which the batch started (UTC).
///
public DateTimeOffset? StartTime { get; set; }
///
/// Time at which the batch finished (UTC)
///
public DateTimeOffset? FinishTime { get; set; }
///
/// List of reports
///
public List? Reports { get; set; }
///
/// List of spawned job ids
///
public List? SpawnedJobs { get; set; }
///
/// User-defined properties for this jobstep.
///
public Dictionary? Properties { get; set; }
}
///
/// The state of a particular run
///
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum JobStepBatchState
{
///
/// Waiting for dependencies of at least one jobstep to complete
///
Waiting = 0,
///
/// Ready to execute
///
Ready = 1,
///
/// Preparing to execute work
///
Starting = 2,
///
/// Executing work
///
Running = 3,
///
/// Preparing to stop
///
Stopping = 4,
///
/// All steps have finished executing
///
Complete = 5
}
#pragma warning disable CA1027
///
/// Error code for a batch not being executed
///
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum JobStepBatchError
{
///
/// No error
///
None = 0,
///
/// The stream for this job is unknown
///
UnknownStream = 1,
///
/// The given agent type for this batch was not valid for this stream
///
UnknownAgentType = 2,
///
/// The pool id referenced by the agent type was not found
///
UnknownPool = 3,
///
/// There are no agents in the given pool currently online
///
NoAgentsInPool = 4,
///
/// There are no agents in this pool that are onlinbe
///
NoAgentsOnline = 5,
///
/// Unknown workspace referenced by the agent type
///
UnknownWorkspace = 6,
///
/// Cancelled
///
Cancelled = 7,
///
/// Lost connection with the agent machine
///
LostConnection = 8,
///
/// Lease terminated prematurely but can be retried.
///
Incomplete = 9,
///
/// An error ocurred while executing the lease. Cannot be retried.
///
ExecutionError = 10,
///
/// The change that the job is running against is invalid
///
UnknownShelf = 11,
///
/// Step was no longer needed during a job update
///
NoLongerNeeded = 12,
///
/// Syncing the branch failed
///
SyncingFailed = 13,
///
/// Legacy alias for
///
[Obsolete("Use SyncingFailed instead")]
AgentSetupFailed = SyncingFailed,
}
#pragma warning restore CA1027
///
/// Request to update a jobstep batch
///
public class UpdateBatchRequest
{
///
/// The new log file id
///
public string? LogId { get; set; }
///
/// The state of this batch
///
public JobStepBatchState? State { get; set; }
}
///
/// Information about a jobstep batch
///
public class GetJobBatchResponse
{
///
/// Unique id for this batch
///
public JobStepBatchId Id { get; set; }
///
/// Index of the group being executed
///
public int GroupIdx { get; set; }
///
/// The agent type
///
public string AgentType { get; set; } = String.Empty;
///
/// The agent assigned to execute this group
///
public string? AgentId { get; set; }
///
/// Rate for using this agent (per hour)
///
public double? AgentRate { get; set; }
///
/// The agent session holding this lease
///
public string? SessionId { get; set; }
///
/// The lease that's executing this group
///
public string? LeaseId { get; set; }
///
/// The unique log file id
///
public string? LogId { get; set; }
///
/// The state of this batch
///
public JobStepBatchState State { get; set; }
///
/// Error code for this batch
///
public JobStepBatchError Error { get; set; }
///
/// The priority of this batch
///
public int WeightedPriority { get; set; }
///
/// Time at which the group started (UTC).
///
public DateTimeOffset? StartTime { get; set; }
///
/// Time at which the group finished (UTC)
///
public DateTimeOffset? FinishTime { get; set; }
///
/// Time at which the group became ready (UTC).
///
public DateTimeOffset? ReadyTime { get; set; }
///
/// Steps within this run
///
public List Steps { get; set; } = new List();
}
///
/// State of an aggregate
///
public enum LabelState
{
///
/// Aggregate is not currently being built (no required nodes are present)
///
Unspecified,
///
/// Steps are still running
///
Running,
///
/// All steps are complete
///
Complete
}
///
/// Outcome of an aggregate
///
public enum LabelOutcome
{
///
/// Aggregate is not currently being built
///
Unspecified,
///
/// A step dependency failed
///
Failure,
///
/// A dependency finished with warnings
///
Warnings,
///
/// Successful
///
Success,
}
///
/// State of a label within a job
///
public class GetLabelStateResponse
{
///
/// Name to show for this label on the dashboard
///
public string? DashboardName { get; set; }
///
/// Category to show this label in on the dashboard
///
public string? DashboardCategory { get; set; }
///
/// Name to show for this label in UGS
///
public string? UgsName { get; set; }
///
/// Project to display this label for in UGS
///
public string? UgsProject { get; set; }
///
/// State of the label
///
public LabelState? State { get; set; }
///
/// Outcome of the label
///
public LabelOutcome? Outcome { get; set; }
///
/// Steps to include in the status of this label
///
public List Steps { get; set; } = new List();
}
///
/// Information about the default label (ie. with inlined list of nodes)
///
public class GetDefaultLabelStateResponse : GetLabelStateResponse
{
///
/// List of nodes covered by default label
///
public List Nodes { get; set; } = new List();
}
///
/// Information about the timing info for a particular target
///
public class GetTimingInfoResponse
{
///
/// Wait time on the critical path
///
public float? TotalWaitTime { get; set; }
///
/// Sync time on the critical path
///
public float? TotalInitTime { get; set; }
///
/// Duration to this point
///
public float? TotalTimeToComplete { get; set; }
///
/// Average wait time by the time the job reaches this point
///
public float? AverageTotalWaitTime { get; set; }
///
/// Average sync time to this point
///
public float? AverageTotalInitTime { get; set; }
///
/// Average duration to this point
///
public float? AverageTotalTimeToComplete { get; set; }
}
///
/// Information about the timing info for a particular target
///
public class GetStepTimingInfoResponse : GetTimingInfoResponse
{
///
/// Name of this node
///
public string? Name { get; set; }
///
/// Average wait time for this step
///
public float? AverageStepWaitTime { get; set; }
///
/// Average init time for this step
///
public float? AverageStepInitTime { get; set; }
///
/// Average duration for this step
///
public float? AverageStepDuration { get; set; }
}
///
/// Information about the timing info for a label
///
public class GetLabelTimingInfoResponse : GetTimingInfoResponse
{
///
/// Name of the label
///
[Obsolete("Use DashboardName instead")]
public string? Name => DashboardName;
///
/// Category for the label
///
[Obsolete("Use DashboardCategory instead")]
public string? Category => DashboardCategory;
///
/// Name of the label
///
public string? DashboardName { get; set; }
///
/// Category for the label
///
public string? DashboardCategory { get; set; }
///
/// Name of the label
///
public string? UgsName { get; set; }
///
/// Category for the label
///
public string? UgsProject { get; set; }
}
///
/// Describes the history of a step
///
public class GetJobStepRefResponse
{
///
/// The job id
///
public JobId JobId { get; set; }
///
/// The batch containing the step
///
public JobStepBatchId BatchId { get; set; }
///
/// The step identifier
///
public JobStepId StepId { get; set; }
///
/// The change number being built
///
[Obsolete("Use CommitId instead")]
public int Change
{
get => _change ?? _commitId?.GetPerforceChangeOrMinusOne() ?? 0;
set => _change = value;
}
int? _change;
///
/// The commit being built
///
public CommitIdWithOrder CommitId
{
get => _commitId ?? CommitIdWithOrder.FromPerforceChange(_change) ?? CommitIdWithOrder.Empty;
set => _commitId = value;
}
CommitIdWithOrder? _commitId;
///
/// The step log id
///
public string? LogId { get; set; }
///
/// The pool id
///
public string? PoolId { get; set; }
///
/// The agent id
///
public string? AgentId { get; set; }
///
/// Outcome of the step, once complete.
///
public JobStepOutcome? Outcome { get; set; }
///
/// The issues which affected this step
///
public List? IssueIds { get; set; }
///
/// The step meta data for this step
///
public List? Metadata { get; set; }
///
/// Time at which the job started
///
public DateTimeOffset JobStartTime { get; set; }
///
/// Time at which the step started.
///
public DateTimeOffset StartTime { get; set; }
///
/// Time at which the step finished.
///
public DateTimeOffset? FinishTime { get; set; }
}
}